Come posso fare un filtro OR in una query Django?


303

Voglio essere in grado di elencare gli elementi che un utente ha aggiunto (sono elencati come creatore) o l'articolo è stato approvato.

Quindi ho sostanzialmente bisogno di selezionare:

item.creator = owner or item.moderated = False

Come lo farei in Django? (preferibilmente con un filtro o un set di query).

Risposte:


545

Esistono Qoggetti che consentono ricerche complesse. Esempio:

from django.db.models import Q

Item.objects.filter(Q(creator=owner) | Q(moderated=False))

6
come è possibile farlo a livello di programmazione? Quindi, ad esempio, puoi averefor f in filters: Item.objects.filter(Q(creator=f1) | Q(creator=f2) | ...)
Alexis,

14
@AlexisK Usa qualcosa di simile reduce(lambda q, f: q | Q(creator=f), filters, Q())per creare il grande oggetto Q.
Phob,

24
@alexis: potresti anche fare Item.objects.filter(creator__in=creators), per esempio.
Kevin London,

4
Se ti chiedi (come me) da dove |viene utilizzato come operatore OR, in realtà è l'operatore Union Set. E 'anche usato (non qui) come OR bit a bit: stackoverflow.com/questions/5988665/pipe-character-in-python
e100

124

È possibile utilizzare il | operatore per combinare direttamente i queryset senza la necessità di oggetti Q:

result = Item.objects.filter(item.creator = owner) | Item.objects.filter(item.moderated = False)

(modifica - Inizialmente non ero sicuro che ciò causasse una query aggiuntiva ma @spookylukey ha sottolineato che la valutazione del queryset pigro se ne occupa)


4
Per scoprire quali query vengono eseguite su una determinata richiesta, è possibile utilizzare l'applicazione Django della barra degli strumenti di debug. È fatto di fantastico e di vittoria.
Deniz Dogan,

25
fai 'from django.db import connection' e usa 'connection.queries'. Ciò richiede DEBUG = True. A proposito, dovresti sapere che i QuerySet sono pigri e questo colpisce il DB solo una volta.
spookylukey,

1
Si potrebbe escludere l'uso con confronti negati?
Neob91,

2
questo può comportare duplicati nel queryset dei risultati?
Charles Haro,

1
Più specificamente, i set di query tendono a colpire il DB solo quando si tenta di indicizzarli, altrimenti si sta semplicemente creando una query.
Awiebe

41

Vale la pena notare che è possibile aggiungere espressioni Q.

Per esempio:

from django.db.models import Q

query = Q(first_name='mark')
query.add(Q(email='mark@test.com'), Q.OR)
query.add(Q(last_name='doe'), Q.AND)

queryset = User.objects.filter(query)

Questo finisce con una query come:

(first_name = 'mark' or email = 'mark@test.com') and last_name = 'doe'

In questo modo non è necessario trattare o operatori, ridurre ecc.


2
Ma è più facile scrivere query |= Q(email='mark@test.com')?
Alex78191,

26

Vuoi rendere dinamico il filtro, quindi devi usare Lambda come

from django.db.models import Q

brands = ['ABC','DEF' , 'GHI']

queryset = Product.objects.filter(reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]))

reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]) è equivalente a

Q(brand=brands[0]) | Q(brand=brands[1]) | Q(brand=brands[2]) | .....

6
Risposta perfetta per me! Per python3, fare from functools import reduceprima.
Dharmit

1
Perché non usare operator.or_invece di lambda x, y: x | y?
Alex78191,

20

Simile alla vecchia Answera, ma un po 'più semplice, senza la lambda:

filter_kwargs = {
    'field_a': 123,
    'field_b__in': (3, 4, 5, ),
}

Per filtrare queste due condizioni usando OR:

Item.objects.filter(Q(field_a=123) | Q(field_b__in=(3, 4, 5, ))

Per ottenere lo stesso risultato a livello di codice:

list_of_Q = [Q(**{key: val}) for key, val in filter_kwargs.items()]
Item.objects.filter(reduce(operator.or_, list_of_Q))

(spezzato in due righe qui, per chiarezza)

operatorè nella libreria standard: import operator
da docstring:

oppure_ (a, b) - Come a | b.

Per Python3, reducenon è più incorporato ma è ancora nella libreria standard:from functools import reduce


PS

Non dimenticare di assicurarti che list_of_Qnon sia vuoto: reduce()si strozzerà nell'elenco vuoto, ha bisogno di almeno un elemento.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.