Як виконати умову АБО у наборі запитів django?


294

Я хочу написати запит Django, еквівалентний цьому SQL-запиту:

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

Як побудувати фільтр набору запитів Django?

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

Це не працює, оскільки це ANDфільтри. Я хочу до ORфільтрів, щоб отримати об'єднання окремих запитів.


Відповіді:


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

через Документацію


Це допоможе, якщо ви додасте друк object.query, щоб ми могли зв'язати як вихід ORM, так і запит для ознайомлення з ним. До речі, чудовий приклад.
Едвін Пац

Чи краще використовувати цей тип запиту або виконати два окремих запити?
MHB

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 Django 2.1 QuerySet API . Більш історичну дискусію можна знайти у квитку DjangoProject № 21333 .


18
"незадокументовані" та "спадщина" злякають мене. Я думаю, що безпечніше використовувати об’єкт Q, про що детально описано у прийнятій відповіді тут.
0атман

2
FYI, order_by () та distct () можуть бути застосовані до
набору

@carruthd спасибі Я це також підтвердив. Відредагує
варильні панелі

Чи можна застосувати order_by () до кожного набору запитів, а потім об'єднати? Так що порядок для кожної умови все ж підтримується? Наприклад, united_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) та & ( І) операторів; зокрема, інакше неможливо використовувати АБО у 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


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.