Comment effectuer la condition OR dans le jeu de requêtes Django?

294

Je veux écrire une requête Django équivalente à cette requête SQL:

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

Comment construire le filtre de jeu de requêtes Django?

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

Cela ne fonctionne pas, car ce ANDsont les filtres. Je veux que ORles filtres obtiennent l'union des ensembles de requêtes individuels.

Elisa
la source

Réponses:

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

via la documentation

Lakshman Prasad
la source
Il serait utile que vous ajoutiez une impression de object.query afin que nous puissions relier à la fois la sortie ORM et Query pour la familiariser. BTW excellent exemple.
Eddwin Paz
Est-il préférable d'utiliser ce type de requête ou d'effectuer deux requêtes distinctes?
MHB
60

Parce que QuerySets implémente l' __or__opérateur Python ( |), ou union, cela fonctionne simplement. Comme on pouvait s'y attendre, l' |opérateur binaire renvoie QuerySetdonc order_by(), .distinct()et d' autres filtres QuerySet peut être clouée à la fin.

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

Mise à jour 2019-06-20: Ceci est maintenant entièrement documenté dans la référence de l'API QuerySet Django 2.1 . Une discussion plus historique peut être trouvée dans le ticket DjangoProject # 21333 .

plaques de cuisson
la source
18
«sans papiers» et «héritage» me font peur. Je pense qu'il est plus sûr d'utiliser l'objet Q, comme détaillé dans la réponse acceptée ici.
0atman le
2
FYI, order_by () et distinct () peuvent être appliqués au jeu de requêtes canalisé après leur combinaison
carruthd
@carruthd merci. Je l'ai également confirmé. Éditera
plaques de cuisson
L'ordonnance_by () peut-elle être appliquée à chaque ensemble de requêtes individuel puis combinée? Pour que l'ordre de chaque condition soit toujours maintenu? Par exemple, combine_queryset = User.objects.filter (revenue__gte = 5000) .order_by ('revenue') | User.objects.filter (revenue__lt = 5000) .order_by ('- revenue')?
impasse
2
@Oatman: | l'opérateur est documenté. Voir docs.djangoproject.com/en/2.0/ref/models/querysets : "En général, les objets Q () permettent de définir et de réutiliser les conditions. Cela permet la construction de requêtes de base de données complexes en utilisant | (OR) et & ( AND); en particulier, il n'est pas possible d'utiliser OR dans QuerySets. " Je n'ai pas vérifié la documentation des versions antérieures mais l'opérateur de pipe fonctionne au moins à partir de Django 1.1.4 (juste essayé).
makeroo
10

Les deux options sont déjà mentionnées dans les réponses existantes:

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

et

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

Cependant, il semble y avoir une certaine confusion quant à la préférence.

Le fait est qu'ils sont identiques au niveau SQL , alors n'hésitez pas à choisir celui que vous aimez!

Le livre de recettes Django ORM en parle en détail, voici la partie pertinente:


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

mène à

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

et

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

mène à

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

source: django-orm-cookbook


jojo
la source