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.