我想写一个Django查询等价于下面的SQL查询:

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

如何构造Django queryset过滤器?

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

这行不通,因为它和过滤器。我想对过滤器进行OR操作以获得单个查询集的并集。


当前回答

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

通过文档

其他回答

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

通过文档

因为QuerySets实现了Python __or__操作符(|)或联合,所以它可以工作。正如您所期望的那样,|二进制操作符返回一个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:现在在Django 2.1 QuerySet API参考中有完整的文档。更多历史性的讨论可以在DjangoProject票号21333中找到。

现有的答案中已经提到了这两个选项:

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

and

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

and

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


类似于@lprsd的答案,你可以使用or_ from操作符库:

from operator import or_
from django.db.models import Q

User.objects.filter(or_(Q(income__gte=5000), Q(income__isnull=True)))

操作符。Or_得到两个值作为参数。要使用三个(或更多)语句,请使用reduce:

from operator import or_
from functools import reduce
from django.db.models import Q

User.objects.filter(reduce(or_, Q(income__gte=5000), Q(income__lt=300), Q(income__isnull=True)))

这种解决方案对于动态生成的查询(例如,使用前端输入的自定义参数)非常有用。

细节

Operator和functools是python标准库,这意味着你不需要安装它们。

正如你在or_函数的文档字符串中看到的,对于a和b参数,它是

和|b一样。

和减少:

将两个参数的函数累加到序列项上, 从左到右,从而将序列简化为单个值。

只是添加这个附加到Q对象的多个过滤器,如果有人可能会寻找它。 如果提供了Q对象,它必须在任何关键字参数的定义之前。否则它是一个无效的查询。你做这件事的时候要小心。

一个例子是

from django.db.models import Q
User.objects.filter(Q(income__gte=5000) | Q(income__isnull=True),category='income')

这里考虑了OR条件和带有收入类别的过滤器