web-dev-qa-db-fra.com

Comment composer dynamiquement un OR filtre de requête dans Django?

Dans un exemple, vous pouvez voir plusieurs filtres de requête OR:

Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))

Par exemple, cela se traduit par:

[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]

Cependant, je veux créer ce filtre de requête à partir d'une liste. Comment faire ça?

par exemple. [1, 2, 3] -> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))

77
Jack Ha

Vous pouvez chaîner vos requêtes comme suit:

values = [1,2,3]

# Turn list of values into list of Q objects
queries = [Q(pk=value) for value in values]

# Take one Q object from the list
query = queries.pop()

# Or the Q object with the ones remaining in the list
for item in queries:
    query |= item

# Query the model
Article.objects.filter(query)
124
Dave Webb

Pour construire des requêtes plus complexes, il est également possible d'utiliser les constantes Q.OR et Q.AND intégrées dans l'objet Q() avec la méthode add () comme suit:

list = [1, 2, 3]
# it gets a bit more complicated if we want to dynamically build
# OR queries with dynamic/unknown db field keys, let's say with a list
# of db fields that can change like the following
# list_with_strings = ['dbfield1', 'dbfield2', 'dbfield3']

# init our q objects variable to use .add() on it
q_objects = Q()

# loop trough the list and create an OR condition for each item
for item in list:
    q_objects.add(Q(pk=item), Q.OR)
    # for our list_with_strings we can do the following
    # q_objects.add(Q(**{item: 1}), Q.OR)

queryset = Article.objects.filter(q_objects)

# sometimes the following is helpful for debugging (returns the SQL statement)
# print queryset.query
63
exside

Une façon plus courte d'écrire la réponse de Dave Webb en utilisant la fonction de réduction de python :

# For Python 3 only
from functools import reduce

values = [1,2,3]

# Turn list of values into one big Q objects  
query = reduce(lambda q,value: q|Q(pk=value), values, Q())  

# Query the model  
Article.objects.filter(query)  
41
Tom Viner
from functools import reduce
from operator import or_
from Django.db.models import Q

values = [1, 2, 3]
query = reduce(or_, (Q(pk=x) for x in values))
26

Peut-être qu'il vaut mieux utiliser l'instruction SQL IN.

Article.objects.filter(id__in=[1, 2, 3])

Voir queryset api reference .

Si vous avez vraiment besoin de faire des requêtes avec une logique dynamique, vous pouvez faire quelque chose comme ça (moche + non testé):

query = Q(field=1)
for cond in (2, 3):
    query = query | Q(field=cond)
Article.objects.filter(query)
19
alex vasi

Voir le docs :

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}

Notez que cette méthode ne fonctionne que pour les recherches de clé primaire, mais cela semble être ce que vous essayez de faire.

Donc ce que vous voulez c'est:

Article.objects.in_bulk([1, 2, 3])
7
Dominic Rodger

Si nous voulons définir par programme le champ de base de données à interroger: 

import operator
questions = [('question__contains', 'test'), ('question__gt', 23 )]
q_list = [Q(x) for x in questions]
Poll.objects.filter(reduce(operator.or_, q_list))
5
zzart

Vous pouvez utiliser l'opérateur | = pour mettre à jour une requête par programme à l'aide d'objets Q.

4
Jeff Ober

En utilisant la solution avec l'opérateur reduce et or_, comment filtrer par champs multiples. 

from functools import reduce
from operator import or_
from Django.db.models import Q

filters = {'field1': [1, 2], 'field2': ['value', 'other_value']}

qs = Article.objects.filter(
   reduce(or_, (Q(**{f'{k}__in': v}) for k, v in filters.items()))
)

p.s. f est un nouveau format de chaîne littéral aded en python3.6

3
Ivan Semochkin

Une autre option dont je n'étais pas au courant jusqu'à récemment - QuerySet remplace également les opérateurs &, |, ~, etc. Les autres réponses indiquent que les objets OR Q sont une meilleure solution à cette question, mais pour des raisons d’intérêt/argument, vous pouvez le faire:

id_list = [1, 2, 3]
q = Article.objects.filter(pk=id_list[0])
for i in id_list[1:]:
    q |= Article.objects.filter(pk=i)

str(q.query) renverra une requête avec tous les filtres de la clause WHERE.

1
Chris

Celui-ci est pour la liste dynamique de pk:

pk_list = qs.values_list('pk', flat=True)  # i.e [] or [1, 2, 3]

if len(pk_list) == 0:
    Article.objects.none()

else:
    q = None
    for pk in pk_list:
        if q is None:
            q = Q(pk=pk)
        else:
            q = q | Q(pk=pk)

    Article.objects.filter(q)
1
Velodee

facile..
depuis Django.db.models, importez Q importer votre modèle args = (Q (visibilité = 1) | (Q (visibilité = 0) & Q (utilisateur = self.user))) #Tuple paramètres = {} #dic order = 'create_at' limite = 10

Models.objects.filter(*args,**parameters).order_by(order)[:limit]
0
alfonsoolavarria