Как выполнить условие ИЛИ в django queryset?

294

Я хочу написать запрос Django, эквивалентный этому запросу SQL:

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

Как построить фильтр наборов запросов Django?

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

Это не работает, потому что это ANDфильтры. Я хочу, чтобы ORфильтры, чтобы получить объединение отдельных наборов запросов.

Elisa
источник
Возможный дубликат Как сделать OR-фильтр в запросе Django?
Джарвис

Ответы:

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

через документацию

Лакшман Прасад
источник
Было бы полезно, если бы вы добавили распечатку object.query, чтобы мы могли связать вывод ORM и Query, чтобы ознакомиться с ним. Кстати отличный пример.
Эддвин Пас
Лучше использовать этот тип запроса или выполнить два отдельных запроса?
МХБ
60

Поскольку QuerySets реализует__or__ оператор Python ( |) или объединение, он просто работает. Как и следовало ожидать, |бинарный оператор возвращает QuerySetтак order_by(), .distinct()и другие фильтры QuerySet может быть добавленным к концу.

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

Обновление 2019-06-20: теперь это полностью документировано в справочнике по API QuerySet Django 2.1 . Более историческое обсуждение можно найти в билете DjangoProject # 21333 .

варочные панели
источник
18
«без документов» и «наследие» меня пугают. Я думаю, что безопаснее использовать объект Q, как подробно описано в принятом ответе здесь.
0atman
2
К вашему сведению, order_by () и different () могут быть применены к конвейерному
набору запросов
@carruthd спасибо. Я это тоже подтвердил. Отредактирую
варки
Можно ли применить order_by () к каждому отдельному набору запросов и затем объединить? Так что заказ на каждое условие еще сохраняется? Например, comb_queryset = User.objects.filter (доход__gte = 5000) .order_by («доход») | User.objects.filter (доход__lt = 5000) .order_by ('- доход')?
тупик
2
@Oatman: | Оператор задокументирован. См. Docs.djangoproject.com/en/2.0/ref/models/querysets : «В общем, объекты Q () позволяют определять и повторно использовать условия. Это позволяет создавать сложные запросы к базе данных, используя | (OR) и & ( Операторы AND), в частности, невозможно использовать OR в QuerySets. " Я не проверял документацию для более ранних версий, но трубач работает как минимум с Django 1.1.4 (только что попробовал).
makeroo
10

Оба варианта уже упомянуты в существующих ответах:

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

и

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

Тем не менее, кажется, что существует некоторая путаница относительно того, какой из них предпочтительнее.

Дело в том, что они одинаковы на уровне SQL , поэтому не стесняйтесь выбирать, что вам нравится!

Django ORM Cookbook говорит довольно подробно об этом, вот соответствующая часть:


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

приводит к

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

и

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

приводит к

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

источник: django-orm-cookbook


Jojo
источник