Jak znaleźć związek dwóch zestawów zapytań Django?

92

Mam model Django z dwoma niestandardowymi metodami menedżera. Każda zwraca inny podzbiór obiektów modelu w oparciu o inną właściwość obiektu.

Czy istnieje sposób, aby uzyskać zestaw zapytań, czy tylko listę obiektów, czyli sumę zestawów zapytań zwróconych przez każdą metodę menedżera?

Paul D. Waite
źródło
3
(Z usuniętej odpowiedzi) Zobacz to pytanie dla odmiany, która działa z QuerySets z różnych modeli: stackoverflow.com/questions/431628/…
rnevius
1
Począwszy od wersji 1.11, zestawy zapytań django mają wbudowaną metodę unii. Dodałem to jako odpowiedź do wykorzystania w przyszłości
Jose Cherian

Odpowiedzi:

179

To działa i wygląda trochę czyściej:

records = query1 | query2

Jeśli nie chcesz duplikatów, musisz dołączyć .distinct():

records = (query1 | query2).distinct()
Jordan Reiter
źródło
5
Podczas gdy zaakceptowana odpowiedź zwraca iterowalną sumę (dokładną listę), tak jak poprosił OP, ta metoda zwraca prawdziwą sumę zestawów zapytań. Ten zestaw zapytań może być dalej obsługiwany, co jest pożądane w wielu okolicznościach.
Krystian Cybulski
5
Z powodu błędu Django ta konstrukcja może czasami zwracać niepoprawne wyniki podczas pracy z ManyToManyFields. Na przykład czasami zobaczysz, że records.count()będzie większe niż query1.count() + query2.count(), co jest wyraźnie niepoprawne.
Jian
4
@Jian Czy możesz wyjaśnić wersję django z błędem i linkiem do problemu z djangoproject?
IMFletcher
10
rekordy = zapytanie1 | zapytanie2; records = records.distinct () dałoby mi poprawny wynik
eugene
5
W Pythonie można przeciążać operatory. Zobacz docs.python.org/2/library/operator.html . Django tworzy więc specjalne metody dla obiektu QuerySet. Zobacz kod tutaj: github.com/django/django/blob/master/django/db/models/...QuerySet klasa udostępnia metody __and__i __or__które nazywane są po &lub |operatorzy są wykorzystywane między dwoma QuerySetprzedmiotami (także stosowanych do Qklasy, jak również ).
Jordan Reiter
49

Począwszy od wersji 1.11 , zestawy zapytań django mają wbudowaną metodę unii.

q = q1.union(q2) #q will contain all unique records of q1 + q2
q = q1.union(q2, all=True) #q will contain all records of q1 + q2 including duplicates
q = q1.union(q2,q3) # more than 2 queryset union

Więcej przykładów znajdziesz w moim wpisie na blogu .

Jose Cherian
źródło
Nie mogłem uzyskać wszystkiego = prawda działa. Skończyło się na rzutowaniu mojego zestawu zapytań do zestawu przed zwróceniem go klientowi.
Braden Holt
1
@BradenHolt, all = True, oznacza, że ​​będzie zawierał zduplikowane rekordy. Możesz po prostu usunąć all = True, aby uniknąć przesyłania go do zestawu.
Jose Cherian
po tym nie działa DjangoFilterBackend, jak mogę używać union i DjangoFilterBackend?
nesalexy
Niestety, wydaje się, że to nie działa w przypadku modeli z domyślną kolejnością zdefiniowaną w Meta modelu. Za każdym razem, gdy próbuję połączyć je z .union, pojawia się następujący błąd: „ORDER BY niedozwolone w podzapytaniach instrukcji złożonych”.
jrial
4

Sugerowałbym użycie „query1.union (query2)” zamiast „query1 | zapytanie2 '; Otrzymałem różne wyniki z powyższych dwóch metod, a pierwsza jest tym, czego się spodziewałem. Oto, co spotkałem:

print "union result:"
for element in query_set1.union(query_set2):
    print element

print "| result:"
for element in (query_set1 | query_set2):
    print element

wynik:

union result:
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object

| result:
KafkaTopic object
KafkaTopic object
Xianxing
źródło
1
Wklej kod, a nie obrazy kodu. Tekstu na obrazach nie można przeszukiwać, nie można go skopiować / wkleić do edytora w celu weryfikacji i zajmuje on więcej miejsca niż to konieczne. Użyj odwrotnych znaków, aby oznaczyć kod jako kod, aby został poprawnie sformatowany. Zobacz łącze „pomoc” obok pola tekstowego.
jrial
Dzięki za aktualizację. :)
jrial