Django'da dinamik olarak bir OR sorgu filtresi nasıl oluşturulur?


104

Bir örnekte birden çok VEYA sorgu filtresi görebilirsiniz:

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

Örneğin, bu şunlarla sonuçlanır:

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

Ancak, bu sorgu filtresini bir listeden oluşturmak istiyorum. Bu nasıl yapılır?

Örneğin [1, 2, 3] -> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))


1
Görünüşe göre bunu iki kez
sormuşsunuz

Bu özel kullanım durumu için Article.objects.filter(pk__in=[1, 2, 3]), muhtemelen modern django'da kullanacaksınız , ancak Q nesnelerini OR'layarak biraz daha gelişmiş bir şey yapmak istiyorsanız, soru yine de geçerlidir.
beruic

Yanıtlar:


162

Sorgularınızı aşağıdaki gibi zincirleyebilirsiniz:

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)

3
Teşekkürler! Benim aradığım buydu :) Yapabileceğinizi bilmiyordum | =
Jack Ha

23
Sorguyu şunu kullanarak da
başlatabilirsiniz

5
Değerlerdeki değer için
2013'te

1
Yukarıdaki gibi isteğe bağlı koşullar eklemek istiyorsanız Django ile nasıl ham sorgular oluşturabilirsiniz?
kullanıcı

Bu benim için işe yaramadı, nedenini bilmiyorum. sorgular benim için sıfır sonuç
döndürüyor

83

Daha karmaşık sorgular oluşturmak için, Q () nesnesinin yerleşik Q.OR ve Q.AND sabitlerini aşağıdaki gibi add () yöntemiyle birlikte kullanma seçeneği de vardır:

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(id__in=[])

# 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

12
Benim gibi bu konuya yeni gelenler için bu cevabın en iyi cevap olarak görülmesi gerektiğini düşünüyorum. Kabul edilen cevaptan daha Djangoesque. Teşekkür ederim!
theresaanna

5
Yerleşik OR ve AND operatörlerini (| ve &) kullanmanın daha pitonik olduğunu tartışacağım. q_objects |= Q(pk=item)
Bobort

Mükemmel! Teşekkür ederim!
RL Shyam

1
Worth eğer belirterek listboş edilecek olur sen eşdeğer dönersiniz Article.objects.all(). Article.objects.none()Yine de bu test için geri dönerek hafifletmek kolaydır .
Wil

2
@Wil q_objectsile de başlatabilirsiniz Q(id__in=[]). Bir şeyle ORed olmadıkça her zaman başarısız olur ve sorgu iyileştirici bunu güzelce halleder.
Jonathan Richards

44

Python'un azaltma işlevini kullanarak Dave Webb'in cevabını yazmanın daha kısa bir yolu :

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

Görünüşe göre "yerleşik" azaltma kaldırılmış ve ile değiştirilmiş functools.reduce. kaynak
lsowen

Teşekkürler @lsowen, düzeltildi.
Tom Viner

operator.or_Lambda yerine kullanmak da mümkündür .
eigenein

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

Tamam ama nereden operatorgeldi?
mpiskore

1
@mpiskore: Diğer tüm Python modülleriyle aynı yer: onu içe aktarırsınız.
Ignacio Vazquez-Abrams

1
komik. gerçekten sorum buydu: hangi modülde / kitaplıkta bulabilirim? google pek yardımcı olmadı.
mpiskore

oh, bir çeşit Django ORM operatörü olduğunu düşündüm. Ne kadar aptalım, teşekkürler!
mpiskore

20

Belki sql IN ifadesini kullanmak daha iyidir.

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

Sorgu kümesi api başvurusuna bakın .

Gerçekten dinamik mantıkla sorgulama yapmanız gerekiyorsa, şöyle bir şey yapabilirsiniz (çirkin + test edilmemiş):

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

1
Ayrıca şunu da kullanabilirsinizquery |= Q(field=cond)
Bobort

8

Dokümanlara bakın :

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

Bu yöntemin yalnızca birincil anahtar aramaları için işe yaradığını, ancak yapmaya çalıştığınız şey bu gibi göründüğünü unutmayın.

Yani istediğiniz şey:

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

6

Sorgulamak istediğimiz db alanını programlı olarak ayarlamak istememiz durumunda:

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

6

Alanları çarparak filtrelemek için kullanan reduceve or_operatörleri kullanan çözüm .

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

ps fyeni bir biçim dizgisidir. Python 3.6'da tanıtıldı


4

Q nesnelerini kullanarak bir sorguyu programlı olarak güncellemek için | = operatörünü kullanabilirsiniz.


2
Bu herhangi bir yerde belgelendi mi? Son 15 dakikadır arıyordum ve bulabildiğim tek şey bu.
wobbily_col

Sektörümüzde olduğu gibi, StackOverflow'da belgelenmiştir!
Chris,

2

Bu, dinamik pk listesi içindir:

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)

Bunun q = Q()yerine kullanabilir ve q = Nonesonra if q is Nonecümleyi kaldırabilirsiniz - biraz daha az verimli ancak üç satırlık kodu kaldırabilirsiniz. (Boş Q, sorgu çalıştırıldığında sonradan birleştirilir.)
Chris

1

Başka bir seçenek Ben yakın zamana kadar farkında değildi - QuerySetaynı zamanda geçersiz kılar &, |, ~, vb operatörleri. OR Q nesnelerinin diğer cevapları bu soru için daha iyi bir çözümdür, ancak ilgi / argüman uğruna şunları yapabilirsiniz:

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)cümledeki tüm filtrelerle bir sorgu döndürür WHERE.


1

Döngü için:

values = [1, 2, 3]
q = Q(pk__in=[]) # generic "always false" value
for val in values:
    q |= Q(pk=val)
Article.objects.filter(q)

Azalt:

from functools import reduce
from operator import or_

values = [1, 2, 3]
q_objects = [Q(pk=val) for val in values]
q = reduce(or_, q_objects, Q(pk__in=[]))
Article.objects.filter(q)

Bunların her ikisi de eşdeğerdir Article.objects.filter(pk__in=values)

valuesBoş olduğunda ne istediğinizi düşünmek önemlidir . Q()Başlangıç ​​değeri olan birçok yanıt her şeyi döndürür . Q(pk__in=[])daha iyi bir başlangıç ​​değeridir. İyileştirici tarafından güzelce işlenen her zaman başarısız olan bir Q nesnesidir (karmaşık denklemler için bile).

Article.objects.filter(Q(pk__in=[]))  # doesn't hit DB
Article.objects.filter(Q(pk=None))    # hits DB and returns nothing
Article.objects.none()                # doesn't hit DB
Article.objects.filter(Q())           # returns everything

Her şeyi boş olduğunda iade etmek istiyorsanız , aşağıdaki davranışı sağlamak için valuesAND ile yapmalısınız ~Q(pk__in=[]):

values = []
q = Q()
for val in values:
    q |= Q(pk=val)
Article.objects.filter(q)                     # everything
Article.objects.filter(q | author="Tolkien")  # only Tolkien

q &= ~Q(pk__in=[])
Article.objects.filter(q)                     # everything
Article.objects.filter(q | author="Tolkien")  # everything

O hatırlamak önemlidir Q()olan şey , değil her zaman başarılı Q nesnesi. İçerdiği herhangi bir işlem onu ​​tamamen bırakacaktır.


0

easy ..
django.db.models'ten içe aktar Q modelinizi içe aktarın args = (Q (visibility = 1) | (Q (visibility = 0) & Q (user = self.user))) #Tuple parameters = {} #dic order = 'create_at' limit = 10

Models.objects.filter(*args,**parameters).order_by(order)[:limit]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.