Como executar a condição OR no django queryset?

294

Eu quero escrever uma consulta Django equivalente a esta consulta SQL:

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

Como construir o filtro queryset do Django?

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

Isso não funciona, porque são ANDos filtros. Quero que ORos filtros obtenham a união de conjuntos de consultas individuais.

Elisa
fonte

Respostas:

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

via documentação

Lakshman Prasad
fonte
Ajudaria se você adicionasse uma impressão de object.query para que possamos relacionar a saída ORM e Query para se familiarizar com ela. Um ótimo exemplo.
Eddwin Paz
É melhor usar esse tipo de consulta ou executar duas consultas separadas?
MHB 16/03
60

Como o QuerySets implementa o __or__operador Python ( |), ou union, ele simplesmente funciona. Como seria de esperar, o |operador binário retorna um QuerySetmodo order_by(), .distinct()e outros filtros queryset podem ser anexados ao fim.

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

Atualização 2019-06-20: Agora isso está totalmente documentado na referência da API do Django 2.1 QuerySet . Discussões mais históricas podem ser encontradas no ticket do DjangoProject # 21333 .

fogão
fonte
18
"não documentado" e "legado" me assustam. Eu acho que é mais seguro usar o objeto Q, conforme detalhado na resposta aceita aqui.
precisa saber é o seguinte
2
FYI, order_by () e distinta () pode ser aplicado ao queryset canalizado depois de serem combinados
carruthd
@carruthd thanks. Eu confirmei isso também. Edit
hobs
O order_by () pode ser aplicado a cada conjunto de consultas individual e depois combinado? Para que a ordem de cada condição ainda seja mantida? Por exemplo, combinado_queryset = Usuário.objetos.filter (renda__gte = 5000) .order_by ('renda') | User.objects.filter (Income__lt = 5000) .order_by ('- renda')?
impasse
2
@Oatman: | operador está documentado. Consulte docs.djangoproject.com/en/2.0/ref/models/querysets : "Em geral, os objetos Q () permitem definir e reutilizar condições. Isso permite a construção de consultas complexas ao banco de dados usando | (OR) e & ( AND); em particular, não é possível usar OU em QuerySets ". Eu não verifiquei a documentação para versões anteriores, mas o operador de pipe trabalha pelo Django 1.1.4 pelo menos (tentei).
makeroo
10

Ambas as opções já são mencionadas nas respostas existentes:

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

e

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

No entanto, parece haver alguma confusão sobre qual deles deve preferir.

O ponto é que eles são idênticos no nível SQL , portanto, fique à vontade para escolher o que quiser!

O Django ORM Cookbook fala com mais detalhes sobre isso, aqui está a parte relevante:


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

leva a

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

e

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

leva a

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

fonte: django-orm-cookbook


jojo
fonte