2010-10-21 4 views
19

Ich versuche AND und OR in einem Filter mit Q-Objekten zu kombinieren. Es sieht so aus, als ob | Benimm dich wie ein AND. Dies bezieht sich auf das vorherige Annotat, das in derselben Abfrage und nicht als Unterabfrage ausgeführt wird.Django-Abfragefilter, der AND und OR mit Q-Objekten kombiniert, liefert nicht die erwarteten Ergebnisse

Wie ist der richtige Umgang mit 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)) 

resultierende SQL-Abfrage

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 

erwartet SQL-Abfrage

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) 

EDIT: Ich habe die erwartete SQL-Abfrage hinzugefügt (ohne auf equipment_subcategory beitreten)

+0

sieht aus wie ein Fehler mir Dies wird generieren. Ich würde einen Fehlerbericht einreichen oder fragen Sie auf #django – tback

Antwort

4

OK, kein Erfolg hier oder auf #django. So wähle ich eine rohe SQL-Abfrage zu verwenden, um dieses Problem zu lösen ...

Hier den Arbeitscode:

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

Try Klammern hinzugefügt explizit Ihre Gruppierung angeben? Wie Sie bereits herausgefunden haben, sind mehrere Params zu filter() nur über UND in das zugrunde liegende SQL verbunden.

Ursprünglich hatte man dies für den Filter:

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

Wenn Sie (A & B) & (C | D) wollte dann sollte diese Arbeit:

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

Ich habe auch diese Lösung versucht, aber es immer noch nicht richtig mit der Anfrage. Angesichts der Dokumentation unter http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects, Filter (Q & Q). Filter (Q | Q) oder Filter (Q & Q , Q | Q) oder Filter (Q & Q & (Q | Q)) sollten sich alle gleich verhalten. Und ich mein Fall, es ist der falsche Weg ... – cgaspoz

+0

Haben Sie es mit etwas versucht, das nicht über annoTate() hinzugefügt wurde? Die Dokumentation für die AND- und OR-Logik für filter() und exclude() ist nicht kugelsicher. Überprüfen Sie daher die tatsächlichen Abfragen. Die Dokumentation zeigt die OR'd-Klauseln in der SQL, aber ich sehe das nicht mit 1.2.3 und sqlite3. Ich sehe es, wenn ich Qa & Qb & (Qc | Qd) mache. – istruble

4

Diese Antwort spät ist aber hilfreich da draußen eine Menge Leute sein könnte.

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

etwas ähnliches wie

WHERE (hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0)) 
Verwandte Themen