Comment puis-je faire un filtre OU dans une requête Django?


303

Je veux pouvoir répertorier les éléments ajoutés par un utilisateur (ils sont répertoriés en tant que créateur) ou l'élément a été approuvé.

J'ai donc essentiellement besoin de sélectionner:

item.creator = owner or item.moderated = False

Comment pourrais-je faire cela dans Django? (de préférence avec un filtre ou un ensemble de requêtes).

Réponses:


545

Il existe des Qobjets qui permettent des recherches complexes. Exemple:

from django.db.models import Q

Item.objects.filter(Q(creator=owner) | Q(moderated=False))

6
comment cela pourrait-il être fait par programme? Ainsi, par exemple pouvoir avoirfor f in filters: Item.objects.filter(Q(creator=f1) | Q(creator=f2) | ...)
Alexis

14
@AlexisK Utilisez quelque chose comme reduce(lambda q, f: q | Q(creator=f), filters, Q())pour créer le grand objet Q.
Phob

24
@alexis: vous pourriez aussi faire Item.objects.filter(creator__in=creators), par exemple.
Kevin London

4
Si vous vous demandez (comme moi) d'où |vient l'utilisation de l'opérateur OR, c'est en fait l'opérateur union défini. Il est également utilisé (pas ici) au niveau du bit OU: stackoverflow.com/questions/5988665/pipe-character-in-python
e100

124

Vous pouvez utiliser le | pour combiner directement des ensembles de requêtes sans avoir besoin d'objets Q:

result = Item.objects.filter(item.creator = owner) | Item.objects.filter(item.moderated = False)

(modifier - J'étais initialement incertain si cela provoquait une requête supplémentaire, mais @spookylukey a souligné que l'évaluation paresseuse du jeu de requêtes s'en occupait)


4
Pour savoir quelles requêtes sont exécutées sur une demande donnée, vous pouvez utiliser l'application Django de la barre d'outils de débogage. C'est fait de génial et de gagner.
Deniz Dogan

25
faites 'depuis django.db import connection' et utilisez 'connection.queries'. Cela nécessite DEBUG = True. BTW, vous devez savoir que les QuerySets sont paresseux et cela ne frappe la base de données qu'une seule fois.
spookylukey

1
L'exclusion peut-elle être utilisée avec des comparaisons négatives?
Neob91

2
cela peut-il entraîner des doublons dans l'ensemble de requêtes de résultat?
Charles Haro

1
Plus précisément, les ensembles de requêtes ont tendance à frapper la base de données uniquement lorsque vous essayez de les indexer, sinon vous créez simplement une requête.
awiebe

41

Il convient de noter qu'il est possible d'ajouter des expressions Q.

Par exemple:

from django.db.models import Q

query = Q(first_name='mark')
query.add(Q(email='mark@test.com'), Q.OR)
query.add(Q(last_name='doe'), Q.AND)

queryset = User.objects.filter(query)

Cela se termine par une requête comme:

(first_name = 'mark' or email = 'mark@test.com') and last_name = 'doe'

De cette façon, il n'est pas nécessaire de traiter avec ou les opérateurs, de réduire etc.


2
Mais c'est plus facile à écrire query |= Q(email='mark@test.com')?
Alex78191

26

Vous voulez rendre le filtre dynamique, alors vous devez utiliser Lambda comme

from django.db.models import Q

brands = ['ABC','DEF' , 'GHI']

queryset = Product.objects.filter(reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]))

reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]) est équivalent à

Q(brand=brands[0]) | Q(brand=brands[1]) | Q(brand=brands[2]) | .....

6
Réponse parfaite pour moi! Pour python3, faites from functools import reduceau préalable.
Dharmit

1
Pourquoi ne pas utiliser à la operator.or_place de lambda x, y: x | y?
Alex78191

20

Semblable aux anciens répondeurs, mais un peu plus simple, sans le lambda:

filter_kwargs = {
    'field_a': 123,
    'field_b__in': (3, 4, 5, ),
}

Pour filtrer ces deux conditions à l'aide de OR:

Item.objects.filter(Q(field_a=123) | Q(field_b__in=(3, 4, 5, ))

Pour obtenir le même résultat par programme:

list_of_Q = [Q(**{key: val}) for key, val in filter_kwargs.items()]
Item.objects.filter(reduce(operator.or_, list_of_Q))

(cassé en deux lignes ici, pour plus de clarté)

operatorest dans la bibliothèque standard: import operator
De docstring:

ou_ (a, b) - Identique à un | b.

Pour Python3, reducen'est plus une fonction intégrée mais est toujours dans la bibliothèque standard:from functools import reduce


PS

N'oubliez pas de vous assurer qu'il list_of_Qn'est pas vide - reduce()s'étouffera sur la liste vide, il a besoin d'au moins un élément.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.