2010-10-21 66 views
19

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

用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查询(不加入上equipment_subcategory)

+0

看起来像一个错误我的东西。我会提交一个错误报告或问#django – tback 2010-10-21 10:18:18

回答

4

OK这里,没有成功或在#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) 
    ') 
20

尝试增加括号来显式指定分组?正如您已经想到的那样,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))) 
+0

我也试过这个解决方案,但它仍然没有正确处理请求。有关http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects的文档,请参阅过滤器(Q&Q).filter(Q | Q)或过滤器(Q&Q) ,Q | Q)或过滤器(Q&Q&(Q | Q))都应该表现相同的方式。而我的情况,这是错误的... – cgaspoz 2010-10-21 17:53:51

+0

你有没有尝试过的东西,而不是通过annotate()添加? filter()和exclude()的AND和OR逻辑文档不是防弹的,因此请继续检查实际查询。该文档显示SQL中的OR'd子句,但我没有看到1.2.3和sqlite3。当我做Qa&Qb&(Qc | Qd)时,我确实看到它。 – istruble 2010-10-21 19:17:08

4

这个答案是迟,但可帮助很多人在那里。

[...].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))