Jak wykonać warunek OR w zestawie zapytań django?

294

Chcę napisać zapytanie Django równoważne temu zapytaniu SQL:

SELECT * from user where income >= 5000 or income is NULL.

Jak zbudować filtr zestawu zapytań Django?

User.objects.filter(income__gte=5000, income=0)

To nie działa, ponieważ są to ANDfiltry. Chcę ORfiltrów, aby uzyskać połączenie poszczególnych zestawów zapytań.

Elisa
źródło

Odpowiedzi:

547
from django.db.models import Q
User.objects.filter(Q(income__gte=5000) | Q(income__isnull=True))

poprzez dokumentację

Lakshman Prasad
źródło
Byłoby to pomocne, jeśli dodasz wydruk object.query, abyśmy mogli powiązać dane wyjściowe ORM i Query w celu zapoznania się z nim. BTW świetny przykład.
Eddwin Paz
Czy lepiej jest użyć tego typu zapytania lub wykonać dwa oddzielne zapytania?
MHB
60

Ponieważ QuerySets implementuje__or__ operator Python ( |) lub union, po prostu działa. Jak można się spodziewać, |operator zwraca binarny QuerySettak order_by(), .distinct()i inne filtry queryset może być dołączona do końca.

combined_queryset = User.objects.filter(income__gte=5000) | User.objects.filter(income__isnull=True)
ordered_queryset = combined_queryset.order_by('-income')

Aktualizacja 2019-06-20: Jest to teraz w pełni udokumentowane w dokumentacji interfejsu API QuerySet Django 2.1 . Bardziej historyczną dyskusję można znaleźć w bilecie DjangoProject # 21333 .

płyty grzewcze
źródło
18
„nieudokumentowane” i „dziedzictwo” przerażają mnie. Myślę, że bezpieczniej jest używać obiektu Q, jak wyszczególniono w zaakceptowanej odpowiedzi tutaj.
0atman
2
Informacje FYI, order_by () i odrębne () można zastosować do potokowego zestawu zapytań po ich połączeniu
carruthd
@carruthd dzięki. Potwierdziłem to również. Edycja
płyty
Czy order_by () można zastosować do każdego zestawu zapytań, a następnie połączyć? Aby kolejność każdego warunku była nadal utrzymywana? Na przykład Combined_queryset = User.objects.filter (dochodowy__gte = 5000). Zamówienie_by („dochód”) | User.objects.filter (profit__lt = 5000) .order_by ('- dochodowy')?
impas
2
@Oatman: | operator jest udokumentowany. Zobacz docs.djangoproject.com/en/2.0/ref/models/querysets : „Zasadniczo obiekty Q () umożliwiają definiowanie i ponowne używanie warunków. Pozwala to na tworzenie złożonych zapytań do bazy danych przy użyciu | (OR) i & ( AND) operatorów; w szczególności nie jest możliwe użycie OR w QuerySets. ” Nie sprawdzałem dokumentacji wcześniejszych wersji, ale operator potoku działa przynajmniej z Django 1.1.4 (właśnie wypróbowałem).
makeroo
10

Obie opcje są już wspomniane w istniejących odpowiedziach:

from django.db.models import Q
q1 = User.objects.filter(Q(income__gte=5000) | Q(income__isnull=True))

i

q2 = User.objects.filter(income__gte=5000) | User.objects.filter(income__isnull=True)

Wydaje się jednak, że istnieje pewne zamieszanie dotyczące tego, który z nich preferować.

Chodzi o to, że są one identyczne na poziomie SQL , więc możesz wybrać, co chcesz!

Django ORM Cookbook mówi dość szczegółowo o tym, tutaj jest odpowiednia część:


queryset = User.objects.filter(
        first_name__startswith='R'
    ) | User.objects.filter(
    last_name__startswith='D'
)

prowadzi do

In [5]: str(queryset.query)
Out[5]: 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login",
"auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name",
"auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff",
"auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user"
WHERE ("auth_user"."first_name"::text LIKE R% OR "auth_user"."last_name"::text LIKE D%)'

i

qs = User.objects.filter(Q(first_name__startswith='R') | Q(last_name__startswith='D'))

prowadzi do

In [9]: str(qs.query)
Out[9]: 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login",
 "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name",
  "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff",
  "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user"
  WHERE ("auth_user"."first_name"::text LIKE R% OR "auth_user"."last_name"::text LIKE D%)'

źródło: django-orm-cookbook


jojo
źródło