Wie mache ich einen ODER-Filter in einer Django-Abfrage?

303

Ich möchte in der Lage sein, die Elemente aufzulisten, die entweder ein Benutzer hinzugefügt hat (sie werden als Ersteller aufgeführt) oder das Element wurde genehmigt.

Also muss ich grundsätzlich auswählen:

item.creator = owner or item.moderated = False

Wie würde ich das in Django machen? (vorzugsweise mit einem Filter oder einem Abfragesatz).

Mez
quelle

Antworten:

544

Es gibt QObjekte, die komplexe Suchvorgänge ermöglichen. Beispiel:

from django.db.models import Q

Item.objects.filter(Q(creator=owner) | Q(moderated=False))
Alex Koshelev
quelle
6
Wie könnte dies programmatisch erfolgen? So zum Beispiel haben könnenfor f in filters: Item.objects.filter(Q(creator=f1) | Q(creator=f2) | ...)
Alexis
14
@AlexisK Verwenden Sie so etwas wie reduce(lambda q, f: q | Q(creator=f), filters, Q())das große Q-Objekt.
Phob
24
@alexis: Sie könnten Item.objects.filter(creator__in=creators)zum Beispiel auch tun .
Kevin London
4
Wenn Sie sich (wie ich) fragen, woher |die Verwendung als ODER-Operator stammt, ist dies tatsächlich der Set-Union-Operator. Es wird auch (nicht hier) als bitweises ODER verwendet: stackoverflow.com/questions/5988665/pipe-character-in-python
e100
124

Sie können die | verwenden Operator zum direkten Kombinieren von Abfragesätzen, ohne dass Q-Objekte benötigt werden:

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

(Bearbeiten - Ich war anfangs nicht sicher, ob dies eine zusätzliche Abfrage verursachte, aber @spookylukey wies darauf hin, dass die verzögerte Abfrage des Abfragesatzes dies erledigt.)

Andy Baker
quelle
4
Um herauszufinden, welche Abfragen für eine bestimmte Anforderung ausgeführt werden, können Sie die Django-Anwendung der Debug-Symbolleiste verwenden. Es ist großartig und gewinnt.
Deniz Dogan
25
Führen Sie 'from django.db import connection' aus und verwenden Sie 'connection.queries'. Dies erfordert DEBUG = True. Übrigens sollten Sie wissen, dass QuerySets faul sind und dies die DB nur einmal trifft.
Spookylukey
1
Könnte Ausschluss bei negierten Vergleichen verwendet werden?
Neob91
2
Kann dies zu Duplikaten in der Ergebnisabfrage führen?
Charles Haro
1
Insbesondere treffen Abfragesätze die Datenbank in der Regel nur dann, wenn Sie versuchen, sie zu indizieren, andernfalls erstellen Sie nur eine Abfrage.
Awiebe
41

Es ist zu beachten, dass es möglich ist, Q- Ausdrücke hinzuzufügen .

Zum Beispiel:

from django.db.models import Q

query = Q(first_name='mark')
query.add(Q(email='[email protected]'), Q.OR)
query.add(Q(last_name='doe'), Q.AND)

queryset = User.objects.filter(query)

Dies endet mit einer Abfrage wie:

(first_name = 'mark' or email = '[email protected]') and last_name = 'doe'

Auf diese Weise gibt es keine Notwendigkeit , zu behandeln oder Betreiber, reduziert die usw.

Marxin
quelle
2
Aber es ist einfacher zu schreiben query |= Q(email='[email protected]')?
Alex78191
26

Wenn Sie den Filter dynamisch machen möchten, müssen Sie Lambda wie verwenden

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]) ist äquivalent zu

Q(brand=brands[0]) | Q(brand=brands[1]) | Q(brand=brands[2]) | .....
Abhishek Chauhan
quelle
6
Perfekte Antwort für mich! Für python3 from functools import reducevorher tun .
Dharmit
1
Warum nicht operator.or_statt verwenden lambda x, y: x | y?
Alex78191
20

Ähnlich wie bei älteren Answera, aber etwas einfacher, ohne das Lambda:

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

So filtern Sie diese beiden Bedingungen mit OR:

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

So erzielen Sie programmgesteuert das gleiche Ergebnis:

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

(aus Gründen der Klarheit in zwei Zeilen unterteilt)

operatorbefindet sich in der Standardbibliothek: import operator
Von docstring:

or_ (a, b) - Wie a | b.

Für Python3 reduceist es nicht mehr integriert, befindet sich aber immer noch in der Standardbibliothek:from functools import reduce


PS

Vergessen Sie nicht, sicherzustellen, dass list_of_Qes nicht leer ist - reduce()wird bei leerer Liste ersticken, es benötigt mindestens ein Element.

frnhr
quelle