2010-10-21 3 views
19

Cerco di combinare AND e OR in un filtro utilizzando oggetti Q. Sembra che il | comportarsi come un AND. Questo è correlato all'annotazione precedente che viene eseguita nella stessa query e non come sottoquery.Il filtro di query Django che combina AND e OR con oggetti Q non restituisce i risultati previsti

Qual è il modo corretto di gestirlo con 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)) 

risultante query 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 

query SQL previsto

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: ho aggiunto la query SQL atteso (senza il join sul equipment_subcategory)

+0

si presenta come un errore per me. Vorrei presentare una segnalazione di bug o chiedere su #django – tback

risposta

4

OK, senza successo qui o su #django. Così ho scelto di utilizzare una query SQL prime per risolvere questo problema ...

Qui il codice di lavoro:

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

Prova ad aggiungere le parentesi per specificare esplicitamente il vostro raggruppamento? Come hai già capito, più parametri a filter() vengono semplicemente uniti tramite AND nell'SQL sottostante.

Originariamente voi ha avuto questo per il filtro:

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

Se si voleva (A & B) & (C | D), allora questo dovrebbe funzionare:

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

Ho provato anche questa soluzione, ma ancora non gestisco correttamente la richiesta. Data la documentazione su http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects, filtro (Q & Q). Filtro (Q | Q) o filtro (Q & Q , Q | Q) o filtro (Q & Q & (Q | Q)) dovrebbero comportarsi tutti allo stesso modo. E io il mio caso, è il modo sbagliato ... – cgaspoz

+0

Hai provato con qualcosa che non viene aggiunto tramite annotate()? La documentazione per la logica AND e OR per filter() ed exclude() non è a prova di proiettile, quindi continua a controllare le query effettive. La documentazione mostra le clausole OR'd in SQL ma non la vedo con 1.2.3 e sqlite3. Lo vedo quando faccio Qa & Qb & (Qc | Qd). – istruble

4

Questa risposta è in ritardo, ma potrebbe essere utile per un sacco di ragazzi là fuori.

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

Questo genererà qualcosa di simile a

WHERE (hide=0 AND deleted=0 AND (T1.qty > 0 OR stock=0)) 
Problemi correlati