2009-04-11 5 views
201

Wie würde ich ein "oder" in einem Django-Filter tun.Django Filter - oder?

Grundsätzlich möchte ich die Elemente in der Liste in der Lage sein, die entweder ein Benutzer hinzugefügt hat (sie werden als Schöpfer aufgeführt) oder das Objekt wurde

so grundsätzlich genehmigt wurde ich

item.creator = owner or item.moderated = False 
auswählen müssen

Wie würde ich tun dies in django (vorzugsweise mit einem Filter/queryset)

Antwort

361

Es gibt Q Objekte, die komplexe Abfragen ermöglichen. Beispiel:

from django.db.models import Q 

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

wie Könnte das programmatisch gemacht werden? Also zum Beispiel in der Lage sein, 'für f in Filter: Item.Objects.Filter (Q (Schöpfer = f1) | Q (Schöpfer = f2) | ...)' – Alexis

+10

@AlexisK Verwenden Sie so etwas wie 'Reduzieren (Lambda q , f: q | Q (Ersteller = f), Filter, Q()) 'um das große Q-Objekt zu erzeugen. – Phob

+16

@alexis: Sie könnten zum Beispiel 'Item.objects.filter (creator__in = Ersteller)' 'machen. –

82

Sie können die | verwenden Betreiber zu kombinieren querysets direkt ohne Objekte Q zu benötigen:

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

(edit - Ich war zunächst nicht sicher, ob dies eine zusätzliche Abfrage verursacht aber @spookylukey wies darauf hin, dass faule queryset Auswertung nimmt diese Aufgabe)

+4

Um herauszufinden, welche Abfragen bei einer bestimmten Anfrage ausgeführt werden, können Sie die Debug-Toolbar verwenden Django-Anwendung. Es ist fantastisch und gewinnt. –

+0

Ich habe dies aus der Shell getestet. Gibt es eine Möglichkeit, die Abfragen für die obige Zeile direkt von der Shell aus zu verfolgen? –

+21

tun 'von django.db Verbindung importieren' und verwenden Sie 'connection.queries'. Dies erfordert DEBUG = True. BTW, sollten Sie wissen, dass [QuerySets sind faul] (https://docs.djangoproject.com/en/dev/topics/db/queries/#querysets-are-lazy) und dies trifft die DB nur einmal. – spookylukey

16

Sie wollen Filter dynamisch zu machen, dann müssen Sie Lambda verwenden wie

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

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

Perfekte Antwort für mich! Für python3, '' from functools import reduce' vorher. – Dharmit

10

ähnlich wie bei älteren answera, aber ein bisschen einfacher, ohne das Lambda:

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

diese beiden Bedingungen filtern OR mit:

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

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

(in zwei Zeilen hier zur besseren Übersicht unterbrochen)

operator ist in der Standardbibliothek: import operator
Von docstring:

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

Für Python3 ist reduzieren nicht in Standard-Bibliothek: from functools import reduce


P. S.

Vergessen Sie nicht sicherzustellen, list_of_Q ist nicht leer - reduce() würgt auf leere Liste, es braucht mindestens ein Element.

9

Es lohnt sich, möglich zu beachten, dass es 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 = Users.objects.filter(query) 

Diese endet mit einer Abfrage nach oben wie:

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

Auf diese Weise gibt es keine Notwendigkeit, mit oder Betreiber umgehen ist, reduzieren die usw.