2010-10-21 63 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))