2009-05-12 10 views
68

Od przykład można zobaczyć wiele lub zapytania filtr:Jak dynamicznie komponować filtr zapytania OR w Django?

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

Na przykład, skutkuje to:

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

Jednak chcę utworzyć ten filtr zapytania z listy. Jak to zrobić?

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

+1

Wygląda na to, że zadałeś to dwa razy: http://stackoverflow.com/questions/852404/ –

+0

W tym konkretnym przypadku użycia prawdopodobnie użyłbyś 'Article.objects .filter (pk__in = [1, 2, 3]) 'we współczesnym django, ale pytanie jest nadal aktualne, jeśli chcesz zrobić coś bardziej zaawansowanego dzięki OR'ingowi Q razem. – beruic

Odpowiedz

107

Mogłeś łańcucha zapytania następująco:

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) 
+2

Dzięki! Tego właśnie szukałem :) Nie wiedziałem, że możesz zrobić | = –

+14

Możesz także zainicjować zapytanie za pomocą: query = Q() – chachan

+3

możesz tworzyć dynamiczne pola za pomocą \ * \ * {'fieldname': value }: ** zapytania = [Q (\ * \ * {'nazwa pola': wartość}) dla wartości w wartościach] ** – rechie

7

Zobacz 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([]) 
{} 

Zauważ, że ta metoda działa tylko dla klucza podstawowego wyszukiwań, ale to wydaje się być to, co próbujesz zrobić.

Więc to, co chcesz:

Article.objects.in_bulk([1, 2, 3]) 
4

Można użyć | = operatora programowo zaktualizować kwerendy przy użyciu obiektów q.

+1

Czy jest to udokumentowane w dowolnym miejscu? Szukałem ostatnich 15 minut i tylko to mogę znaleźć. –

+0

Podobnie jak w naszej branży, jest to udokumentowane w StackOverflow! – Chris

18

Może lepiej użyć instrukcji SQL IN.

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

Zobacz queryset api reference.

Jeśli naprawdę potrzebujesz, aby zapytania z dynamicznym logiki, można zrobić coś takiego (brzydki + nie testowane):

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

Możesz również użyć 'query | = Q (field = cond)' – Bobort

39

krótsza droga pisania odpowiedzi Dave Webb Korzystanie python's reduce function:

# 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) 
+0

Wygląda na to, że "wbudowana" redukcja została usunięta i zastąpiona przez 'functools.reduce'. [źródło] (https://docs.python.org/3.0/whatsnew/3.0.html#builtins) – lsowen

+0

Dzięki @lsowen, poprawiono. –

24
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)) 
+0

OK, ale skąd pochodzi 'operator'? – mpiskore

+0

@mpiskore: To samo miejsce, co każdy inny moduł Pythona: importujesz go. –

+0

śmieszne. to było naprawdę moje pytanie: w jakim module/bibliotece mogę go znaleźć? google niewiele mi pomógł. – mpiskore

5

W przypadku chcemy programowo ustawić co db pole chcemy zapytać:

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

Ten jest dla dynamicznej listy 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) 
+0

Możesz użyć 'q = Q()' zamiast 'q = None', a następnie usunąć klauzulę' if q is Brak '- nieco mniej wydajną, ale może usunąć trzy linie kodu. (Pusty Q jest następnie usuwany, gdy kwerenda jest uruchamiana.) – Chris

48

do budowania bardziej złożonych zapytań istnieje również opcja korzystać z wbudowanych w Q (stałych) obiektu Q.OR i Q.AND razem z metody add() tak:

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 
+6

Dla nowo przybyłych do tego wątku, tak jak ja, myślę, że ta odpowiedź powinna być uważana za najwyższą odpowiedź. Jest bardziej Djangoosque niż zaakceptowana odpowiedź. Dziękuję Ci! – theresaanna

+3

Chciałbym debatować, że jest bardziej pythonic używać wbudowanych operatorów OR i AND (| i &). 'q_objects | = Q (pk = item)' – Bobort

+0

Idealny! Dziękuję Ci! –

0

łatwe ..
from import django.db.models Q importujesz model args = (Q (widoczność = 1) | (Q (widoczność = 0) & Q (użytkownik = self.użytkowników))) #Tuple parametry = {} #dic order = 'create_at' limit = 10

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

Inna opcja nie była świadoma do niedawna - QuerySet zastępuje również &, |, ~ itp. operatorzy. Inne odpowiedzi, że obiekty lub Q są lepszym rozwiązaniem na to pytanie, ale ze względu na odsetek/argumentu, można zrobić:

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) powróci jedno zapytanie ze wszystkich filtrów w klauzuli WHERE.

1

Korzystanie z rozwiązania z reduce i or_ tutaj, jak można filtrować przez pomnożenie pól.

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'{field}__in': list_filters[field]}) 
     for field in list_filters 
     ) 
    ) 
) 

p.s. f to nowy format napisów napisany dosłownie w python3.6