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).

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))

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.)


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='mark@test.com'), 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 = 'mark@test.com') and last_name = 'doe'

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


2
Aber es ist einfacher zu schreiben query |= Q(email='mark@test.com')?
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]) | .....

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.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.