425 votes

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

Je veux pouvoir lister les éléments ajoutés par un utilisateur (ils sont répertoriés comme créateur) ou les éléments qui ont été approuvés.

Donc, je dois essentiellement sélectionner:

item.creator = propriétaire or item.moderated = False

Comment puis-je faire cela en Django? (de préférence avec un filtre ou un queryset).

747voto

Alex Koshelev Points 5522

Il existe Q objets qui permettent des recherches complexes. Exemple:

from django.db.models import Q

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

10 votes

Comment cela pourrait-il être fait de manière programmable? Ainsi, par exemple, être capable d'avoir pour f dans filtres: objets d'Article.filter(Q(creator=f1) | Q(creator=f2) | ...)

15 votes

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

33 votes

@alexis: vous pourriez également faire Item.objects.filter(creator__in=creators), par exemple.

159voto

andybak Points 7081

Vous pouvez utiliser l'opérateur | pour combiner directement des querysets sans avoir besoin d'objets Q :

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

(edit - J'étais initialement incertain si cela provoquait une requête supplémentaire mais @spookylukey a souligné que l'évaluation différée du queryset s'en charge)

4 votes

Pour savoir quelles requêtes sont exécutées sur une demande donnée, vous pouvez utiliser l'application Django debug-toolbar. C'est génial et ça gagne.

0 votes

J'ai testé cela à partir de la coquille. Y a-t-il un moyen de tracer les requêtes pour la ligne ci-dessus directement à partir de la coquille?

32 votes

Exécutez 'from django.db import connection' et utilisez 'connection.queries'. Cela nécessite DEBUG=True. Au fait, vous devez savoir que les QuerySets sont paresseux et cela interroge la base de données une seule fois.

68voto

bzykubd Points 469

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 suit :

(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 les opérateurs or, les réductions, etc.

5 votes

Mais il est plus facile d'écrire query |= Q(email='mark@test.com') ?

2 votes

@Alex78191, les gens ont des préférences de style de codage différentes, mais en plus de cela, cette utilisation permet à l'opérateur (Q.OR ou Q.AND) d'être donné en argument à une fonction qui peut être amenée à gérer les deux scénarios.

38voto

Abhishek Chauhan Points 854

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]) équivaut à

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

7 votes

Réponse parfaite pour moi! Pour python3, faites from functools import reduce au préalable.

3 votes

Pourquoi ne pas utiliser operator.or_ au lieu de lambda x, y: x | y?

27voto

frnhr Points 2880

Tout comme les réponses précédentes, mais un peu plus simple, sans le lambda...

Pour filtrer ces deux conditions en utilisant OU:

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

Pour obtenir le même résultat de manière programmatique:

filter_kwargs = {
    'field_a': 123,
    'field_b__in': (3, 4, 5, ),
}
list_of_Q = [Q(**{key: val}) for key, val in filter_kwargs.items()]
Item.objects.filter(reduce(operator.or_, list_of_Q))

operator est dans la bibliothèque standard: import operator
À partir de la documentation:

ou_(a, b) -- Similaire à a | b.

Pour Python3, reduce n'est plus intégré mais il reste dans la bibliothèque standard: from functools import reduce


P.S.

N'oubliez pas de vous assurer que list_of_Q n'est pas vide - reduce() va bloquer sur une liste vide, il a besoin d'au moins un élément.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X