【问题标题】:Django query filter combining AND and OR with Q objects don't return the expected results将 AND 和 OR 与 Q 对象组合的 Django 查询过滤器不返回预期结果
【发布时间】:2011-04-28 12:22:50
【问题描述】:

我尝试使用 Q 对象在过滤器中组合 AND 和 OR。看起来 |表现得像一个 AND。这与之前在同一查询中运行的注释有关,而不是作为子查询。

用 Django 处理这个问题的正确方法是什么?

models.py

class Type(models.Model):
    name = models.CharField(_('name'), max_length=100)
    stock = models.BooleanField(_('in stock'), default=True)
    hide = models.BooleanField(_('hide'), default=False)
    deleted = models.BooleanField(_('deleted'), default=False)

class Item(models.Model):
    barcode = models.CharField(_('barcode'), max_length=100, blank=True)
    quantity = models.IntegerField(_('quantity'), default=1)
    type = models.ForeignKey('Type', related_name='items', verbose_name=_('type'))

views.py

def hire(request):
    categories_list = Category.objects.all().order_by('sorting')
    types_list = Type.objects.annotate(quantity=Sum('items__quantity')).filter(
        Q(hide=False) & Q(deleted=False),
        Q(stock=False) | Q(quantity__gte=1))
    return render_to_response('equipment/hire.html', {
           'categories_list': categories_list,
           'types_list': types_list,
           }, context_instance=RequestContext(request))

生成的 SQL 查询

SELECT "equipment_type"."id" [...] FROM "equipment_type" LEFT OUTER JOIN
    "equipment_subcategory" ON ("equipment_type"."subcategory_id" =
    "equipment_subcategory"."id") LEFT OUTER JOIN "equipment_item" ON
    ("equipment_type"."id" = "equipment_item"."type_id") WHERE 
    ("equipment_type"."hide" = False AND "equipment_type"."deleted" = False )
    AND ("equipment_type"."stock" = False )) GROUP BY "equipment_type"."id"
    [...] HAVING SUM("equipment_item"."quantity") >= 1

预期的 SQL 查询

SELECT
    *
FROM
    equipment_type
LEFT JOIN (
    SELECT type_id, SUM(quantity) AS qty
    FROM equipment_item
    GROUP BY type_id
) T1
ON id = T1.type_id
WHERE hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0)

编辑:我添加了预期的 SQL 查询(没有设备子类别的连接)

【问题讨论】:

  • 对我来说似乎是个错误。我会提交错误报告或在#django 上询问

标签: python django django-q


【解决方案1】:

这是我的复杂查询示例,希望对您有所帮助

or_condition = Q()
and_condition = Q(company=request.user.profile.company)

for field in MyModel._meta.get_fields():
    if field.name != 'created_on' and field.name != 'company':
        or_condition.add(
            Q(**{"{}__icontains".format(field.name): query}), Q.OR)

and_condition.add(or_condition2, Q.AND)
MyModel.objects.filter(and_condition)

这种方法的问题是你的or_condition 中出现了empty (AND: ) 的情况。它根本不影响查询,但它让我烦恼! 我目前的解决方案如下

import operator
from functools import reduce

and_condition = Q(company=request.user.profile.company)
or_condition = reduce(operator.or_, (Q(**{"{}__icontains".format(field.name): query})
                                     for field in MyModel._meta.get_fields() if field.name != 'created_on' and field.name != 'company'))

and_condition.add(or_condition, Q.AND)
MyModel.objects.filter(and_condition)

【讨论】:

    【解决方案2】:

    这个答案很晚,但可能对很多人有帮助。

    [...].filter(hide=False & deleted=False)
    .filter(Q(stock=False) | Q(quantity__gte=1))
    

    这将产生类似于

    的东西
    WHERE (hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0))
    

    【讨论】:

    • .. 还有女孩。
    【解决方案3】:

    好的,这里或#django 上没有成功。所以我选择使用原始 SQL 查询来解决这个问题...

    这里是工作代码:

    types_list = Type.objects.raw('SELECT * FROM equipment_type
        LEFT JOIN (                                            
            SELECT type_id, SUM(quantity) AS qty               
            FROM equipment_item                                
            GROUP BY type_id                                   
        ) T1                                                   
        ON id = T1.type_id                                     
        WHERE hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0) 
        ')
    

    【讨论】:

      【解决方案4】:

      尝试添加括号以明确指定您的分组?正如您已经发现的那样,filter() 的多个参数只是通过底层 SQL 中的 AND 连接起来的。

      最初你有这个过滤器:

      [...].filter(
          Q(hide=False) & Q(deleted=False),
          Q(stock=False) | Q(quantity__gte=1))
      

      如果你想要 (A & B) & (C | D) 那么这应该可行:

      [...].filter(
          Q(hide=False) & Q(deleted=False) &
          (Q(stock=False) | Q(quantity__gte=1)))
      

      【讨论】:

      • 我也试过这个解决方案,但它仍然不能正确处理请求。鉴于docs.djangoproject.com/en/dev/topics/db/queries/… 的文档,filter(Q&Q).filter(Q|Q) 或 filter(Q&Q, Q|Q) 或 filter(Q&Q&(Q|Q)) 的行为方式都应该相同。我的情况是,这是错误的方式......
      • 您是否尝试过未通过 annotate() 添加的内容? filter() 和 exclude() 的 AND 和 OR 逻辑的文档不是防弹的,因此请继续检查实际查询。该文档显示了 SQL 中的 OR'd 子句,但我在 1.2.3 和 sqlite3 中看不到这一点。当我做 Qa & Qb & (Qc | Qd) 时,我确实看到了。
      猜你喜欢
      • 1970-01-01
      • 2011-09-08
      • 2018-11-18
      • 1970-01-01
      • 2018-09-02
      • 1970-01-01
      • 2014-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多