【问题标题】:How to dynamically compose an OR query filter in Django?如何在 Django 中动态组合 OR 查询过滤器?
【发布时间】:2010-10-25 13:01:08
【问题描述】:

从一个例子你可以看到一个多重 OR 查询过滤器:

Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))

例如,这会导致:

[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]

但是,我想从列表中创建此查询过滤器。该怎么做?

例如[1, 2, 3] -&gt; Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))

【问题讨论】:

  • 您似乎已经问过两次:stackoverflow.com/questions/852404
  • 对于这个特定的用例,您可能会在现代 django 中使用 Article.objects.filter(pk__in=[1, 2, 3]),但如果您想通过 OR'ing Q 对象一起做一些更高级的事情,这个问题仍然是相关的。

标签: python django django-q


【解决方案1】:

docs

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}

请注意,此方法仅适用于主键查找,但这似乎是您想要做的。

所以你想要的是:

Article.objects.in_bulk([1, 2, 3])

【讨论】:

    【解决方案2】:

    您可以使用 |= 运算符以编程方式使用 Q 对象更新查询。

    【讨论】:

    • 这是否记录在任何地方?我一直在寻找最后 15 分钟,这是我唯一能找到的。
    • 就像我们行业中的很多人一样,它记录在 StackOverflow 上!
    【解决方案3】:

    也许使用sql IN语句更好。

    Article.objects.filter(id__in=[1, 2, 3])
    

    queryset api reference

    如果你真的需要用动态逻辑进行查询,你可以这样做(丑陋+未测试):

    query = Q(field=1)
    for cond in (2, 3):
        query = query | Q(field=cond)
    Article.objects.filter(query)
    

    【讨论】:

    • 你也可以使用query |= Q(field=cond)
    【解决方案4】:

    您可以将查询链接如下:

    values = [1,2,3]
    
    # Turn list of values into list of Q objects
    queries = [Q(pk=value) for value in values]
    
    # Take one Q object from the list
    query = queries.pop()
    
    # Or the Q object with the ones remaining in the list
    for item in queries:
        query |= item
    
    # Query the model
    Article.objects.filter(query)
    

    【讨论】:

    • 谢谢!这就是我要找的:) 不知道你能做到 |=
    • 您也可以使用以下方法初始化查询:query = Q()
    • 您可以使用 **{'fieldname': value} 创建动态字段:queries = [Q(**{'fieldname': value}) for value in values]
    • 如果您想添加上述可选条件,如何使用 Django 编写原始查询?
    • 这对我不起作用,我不知道为什么。查询为我返回零结果
    【解决方案5】:

    使用python's reduce function 编写 Dave Webb 答案的更短方式:

    # For Python 3 only
    from functools import reduce
    
    values = [1,2,3]
    
    # Turn list of values into one big Q objects  
    query = reduce(lambda q,value: q|Q(pk=value), values, Q())  
    
    # Query the model  
    Article.objects.filter(query)  
    

    【讨论】:

    • 看起来“内置”reduce 已被删除并替换为functools.reducesource
    • 感谢@lsowen,已修复。
    • 并且可以使用operator.or_代替lambda。
    【解决方案6】:
    from functools import reduce
    from operator import or_
    from django.db.models import Q
    
    values = [1, 2, 3]
    query = reduce(or_, (Q(pk=x) for x in values))
    

    【讨论】:

    • 好的,但是operator 是从哪里来的?
    • @mpiskore:与其他所有 Python 模块相同的位置:您导入它。
    • 好笑。这真的是我的问题:我可以在哪个模块/库中找到它?谷歌没有太大帮助。
    • 哦,我还以为是某种 Django ORM 运算符。我真傻,谢谢!
    【解决方案7】:

    如果我们想以编程方式设置我们要查询的数据库字段:

    import operator
    questions = [('question__contains', 'test'), ('question__gt', 23 )]
    q_list = [Q(x) for x in questions]
    Poll.objects.filter(reduce(operator.or_, q_list))
    

    【讨论】:

      【解决方案8】:

      这个是动态pk列表的:

      pk_list = qs.values_list('pk', flat=True)  # i.e [] or [1, 2, 3]
      
      if len(pk_list) == 0:
          Article.objects.none()
      
      else:
          q = None
          for pk in pk_list:
              if q is None:
                  q = Q(pk=pk)
              else:
                  q = q | Q(pk=pk)
      
          Article.objects.filter(q)
      

      【讨论】:

      • 您可以使用q = Q() 代替q = None,然后删除if q is None 子句——效率稍低,但可以删除三行代码。 (运行查询时,空的 Q 随后会被合并掉。)
      【解决方案9】:

      要构建更复杂的查询,还可以选择使用内置 Q() 对象的常量 Q.OR 和 Q.AND 以及 add() 方法,如下所示:

      list = [1, 2, 3]
      # it gets a bit more complicated if we want to dynamically build
      # OR queries with dynamic/unknown db field keys, let's say with a list
      # of db fields that can change like the following
      # list_with_strings = ['dbfield1', 'dbfield2', 'dbfield3']
      
      # init our q objects variable to use .add() on it
      q_objects = Q(id__in=[])
      
      # loop trough the list and create an OR condition for each item
      for item in list:
          q_objects.add(Q(pk=item), Q.OR)
          # for our list_with_strings we can do the following
          # q_objects.add(Q(**{item: 1}), Q.OR)
      
      queryset = Article.objects.filter(q_objects)
      
      # sometimes the following is helpful for debugging (returns the SQL statement)
      # print queryset.query
      

      【讨论】:

      • 对于这个话题的新手,和我一样,我认为这个答案应该被视为最佳答案。它比公认的答案更 Djangoesque。谢谢!
      • 我认为使用内置 OR 和 AND 运算符(| 和 &)更符合 Python 风格。 q_objects |= Q(pk=item)
      • 完美!谢谢!
      • 值得注意的是,如果list 恰好为空,您将返回与Article.objects.all() 等效的值。不过,通过为该测试返回 Article.objects.none() 可以轻松缓解。
      • @Wil 你也可以用Q(id__in=[]) 初始化q_objects。它总是会失败,除非对某些东西进行 ORed 并且查询优化器会很好地处理它。
      【解决方案10】:

      简单..
      从 django.db.models 导入 Q 导入你的模型 args = (Q(visibility=1)|(Q(visibility=0)&Q(user=self.user))) #Tuple 参数={} #dic 订单='create_at' 限制 = 10

      Models.objects.filter(*args,**parameters).order_by(order)[:limit]
      

      【讨论】:

        【解决方案11】:

        直到最近我才知道的另一个选项 - QuerySet 也覆盖了 &amp;|~ 等运算符。 OR Q 对象的其他答案是这个问题的更好解决方案,但为了感兴趣/争论,您可以这样做:

        id_list = [1, 2, 3]
        q = Article.objects.filter(pk=id_list[0])
        for i in id_list[1:]:
            q |= Article.objects.filter(pk=i)
        

        str(q.query) 将返回一个包含WHERE 子句中所有过滤器的查询。

        【讨论】:

          【解决方案12】:

          使用reduceor_ 运算符按乘法字段过滤的解决方案。

          from functools import reduce
          from operator import or_
          from django.db.models import Q
          
          filters = {'field1': [1, 2], 'field2': ['value', 'other_value']}
          
          qs = Article.objects.filter(
             reduce(or_, (Q(**{f'{k}__in': v}) for k, v in filters.items()))
          )
          

          附言f 是一种新的格式字符串文字。它是在 python 3.6 中引入的

          【讨论】:

            【解决方案13】:

            for循环:

            values = [1, 2, 3]
            q = Q(pk__in=[]) # generic "always false" value
            for val in values:
                q |= Q(pk=val)
            Article.objects.filter(q)
            

            减少:

            from functools import reduce
            from operator import or_
            
            values = [1, 2, 3]
            q_objects = [Q(pk=val) for val in values]
            q = reduce(or_, q_objects, Q(pk__in=[]))
            Article.objects.filter(q)
            

            这两个都等价于Article.objects.filter(pk__in=values)

            values 为空时,请务必考虑您想要什么。许多以Q() 为起始值的答案将返回everythingQ(pk__in=[]) 是一个更好的起始值。它是一个总是失败的 Q 对象,优化器可以很好地处理(即使对于复杂的方程)。

            Article.objects.filter(Q(pk__in=[]))  # doesn't hit DB
            Article.objects.filter(Q(pk=None))    # hits DB and returns nothing
            Article.objects.none()                # doesn't hit DB
            Article.objects.filter(Q())           # returns everything
            

            如果您想要values 为空时返回所有内容,您应该与~Q(pk__in=[]) 一起使用以确保该行为:

            values = []
            q = Q()
            for val in values:
                q |= Q(pk=val)
            Article.objects.filter(q)                     # everything
            Article.objects.filter(q | author="Tolkien")  # only Tolkien
            
            q &= ~Q(pk__in=[])
            Article.objects.filter(q)                     # everything
            Article.objects.filter(q | author="Tolkien")  # everything
            

            重要的是要记住Q()nothing,而不是始终成功的 Q 对象。任何涉及它的操作都会完全丢弃它。

            【讨论】:

              【解决方案14】:

              找到动态字段名称的解决方案

              def search_by_fields(value, queryset, search_in_fields):
                  if value:
                      value = value.strip()
              
                  if value:
                      query = Q()
                      for one_field in search_in_fields:
                          query |= Q(("{}__icontains".format(one_field), value))
              
                      queryset = queryset.filter(query)
              
                  return queryset
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-10-18
                • 1970-01-01
                • 1970-01-01
                • 2020-11-13
                • 1970-01-01
                • 2010-09-26
                相关资源
                最近更新 更多