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).

Mez
la source

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))
Alex Koshelev
la source
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)

Andy Baker
la source
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='[email protected]'), 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 = '[email protected]') 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.

marxin
la source
2
Mais c'est plus facile à écrire query |= Q(email='[email protected]')?
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]) | .....
Abhishek Chauhan
la source
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.

frnhr
la source