จะเขียนตัวกรองการสืบค้น OR ใน Django แบบไดนามิกได้อย่างไร?


104

จากตัวอย่างคุณจะเห็นตัวกรองคำค้นหา OR หลายตัว:

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

ตัวอย่างเช่นผลลัพธ์ใน:

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

อย่างไรก็ตามฉันต้องการสร้างตัวกรองคำค้นหานี้จากรายการ ต้องทำอย่างไร?

เช่น [1, 2, 3] -> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))


1
ดูเหมือนคุณจะถามสิ่งนี้สองครั้ง: stackoverflow.com/questions/852404
Dominic Rodger

สำหรับกรณีการใช้งานที่เฉพาะเจาะจงนี้คุณอาจใช้Article.objects.filter(pk__in=[1, 2, 3])ใน django สมัยใหม่ แต่คำถามยังคงเกี่ยวข้องหากคุณต้องการทำอะไรขั้นสูงขึ้นอีกเล็กน้อยโดย OR'ing Q ด้วยกัน
beruic

คำตอบ:


162

คุณสามารถเชื่อมโยงการสืบค้นของคุณได้ดังนี้:

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
ขอบคุณ! นี่คือสิ่งที่ฉันกำลังมองหา :) ไม่รู้ว่าคุณทำได้ | =
แจ็คฮา

23
คุณสามารถเริ่มต้นการสืบค้นโดยใช้: query = Q ()
chachan

5
คุณสามารถสร้างฟิลด์ไดนามิกโดยใช้ ** {'fieldname': value}: queries = [Q (** {'fieldname': value}) สำหรับค่าในค่า]
rechie

1
คุณจะเขียนแบบสอบถามดิบด้วย Django ได้อย่างไรหากคุณต้องการเพิ่มเงื่อนไขที่เป็นทางเลือกดังกล่าวข้างต้น
ผู้ใช้

นั่นไม่ได้ผลสำหรับฉันฉันไม่รู้ว่าทำไม การค้นหาส่งคืนผลลัพธ์เป็นศูนย์สำหรับฉัน
Mehran Nouri

83

ในการสร้างแบบสอบถามที่ซับซ้อนยิ่งขึ้นนอกจากนี้ยังมีตัวเลือกในการใช้ค่าคงที่ของอ็อบเจ็กต์ Q () ในตัว Q.OR และ Q.AND ร่วมกับวิธีการ add () ดังนี้:

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
สำหรับผู้มาใหม่ในกระทู้นี้เช่นผมคิดว่าคำตอบนี้ควรถือเป็นคำตอบอันดับต้น ๆ Djangoesque เป็นมากกว่าคำตอบที่ยอมรับ ขอบคุณ!
theresaanna

5
ฉันจะอภิปรายว่ามันเป็น pythonic มากกว่าที่จะใช้ตัวดำเนินการ builtin OR และ AND (| และ &) q_objects |= Q(pk=item)
Bobort

สมบูรณ์แบบ! ขอบคุณ!
RL Shyam

1
เป็นที่น่าสังเกตว่าหากlistว่างเปล่าคุณจะคืนค่าเท่ากับArticle.objects.all(). ง่ายต่อการบรรเทาด้วยการกลับไปArticle.objects.none()ทดสอบครั้งนั้น
Wil

2
@ คุณสามารถเริ่มต้นq_objectsด้วยQ(id__in=[]). มันจะล้มเหลวเสมอเว้นแต่จะ ORed ด้วยบางสิ่งบางอย่างและเครื่องมือเพิ่มประสิทธิภาพการสืบค้นจะจัดการกับมันอย่างดี
Jonathan Richards

44

วิธีที่สั้นกว่าในการเขียนคำตอบของ Dave Webb โดยใช้ฟังก์ชันลดของ 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)  

ดูเหมือนว่า "ในตัว" functools.reduceลดถูกลบออกไปและแทนที่ด้วย ที่มา
lsowen

ขอบคุณ @lsowen แก้ไข
Tom Viner

และเป็นไปได้ที่จะใช้operator.or_แทนแลมด้า
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))

ตกลง แต่มันoperatorมาจากไหน?
mpiskore

1
@mpiskore: ที่เดียวกับโมดูล Python อื่น ๆ : คุณนำเข้า
Ignacio Vazquez-Abrams

1
ตลก. นั่นเป็นคำถามของฉันจริงๆ: ฉันสามารถหาโมดูล / ไลบรารีได้ในโมดูลใด google ไม่ได้ช่วยอะไรมาก
mpiskore

โอ้ฉันคิดว่ามันเป็นตัวดำเนินการ Django ORM ฉันโง่แค่ไหนขอบคุณ!
mpiskore

20

อาจจะดีกว่าถ้าใช้คำสั่ง sql IN

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

ดูอ้างอิง API ชุดข้อความค้นหา

หากคุณต้องการสร้างแบบสอบถามด้วยตรรกะแบบไดนามิกจริงๆคุณสามารถทำสิ่งนี้ได้ (น่าเกลียด + ไม่ผ่านการทดสอบ):

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

1
คุณยังสามารถใช้query |= Q(field=cond)
Bobort

8

ดูเอกสาร :

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

โปรดทราบว่าวิธีนี้ใช้ได้กับการค้นหาคีย์หลักเท่านั้น แต่ดูเหมือนว่าคุณกำลังพยายามทำอยู่

ดังนั้นสิ่งที่คุณต้องการคือ:

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

6

ในกรณีที่เราต้องการตั้งค่าโดยใช้โปรแกรม db field ที่เราต้องการสอบถาม:

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

โซลูชันที่ใช้reduceและor_ตัวดำเนินการในการกรองตามเขตข้อมูลการคูณ

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 fเป็นสตริงรูปแบบใหม่ ถูกนำมาใช้ใน python 3.6


4

คุณสามารถใช้ตัวดำเนินการ | = เพื่ออัปเดตแบบสอบถามทางโปรแกรมโดยใช้ออบเจ็กต์ Q


2
เอกสารนี้อยู่ที่ใด ฉันค้นหาในช่วง 15 นาทีที่ผ่านมาและนี่คือสิ่งเดียวที่ฉันพบ
wobbily_col

เช่นเดียวกับในอุตสาหกรรมของเรามีการบันทึกไว้ใน StackOverflow!
คริส

2

อันนี้ใช้สำหรับรายการ 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)

คุณสามารถใช้q = Q()แทนจากq = Noneนั้นลบif q is Noneอนุประโยค - มีประสิทธิภาพน้อยกว่าเล็กน้อย แต่สามารถลบโค้ดสามบรรทัดได้ (Q ว่างจะถูกรวมเข้าด้วยกันเมื่อเรียกใช้แบบสอบถาม)
คริส

1

อีกตัวเลือกหนึ่งที่ผมไม่ได้ตระหนักถึงจนกระทั่งเมื่อเร็ว ๆ - QuerySetยังแทนที่&, |, ~ฯลฯ ผู้ประกอบการ คำตอบอื่น ๆ ที่ OR Q เป็นคำตอบที่ดีกว่าสำหรับคำถามนี้ แต่เพื่อประโยชน์ของผลประโยชน์ / การโต้แย้งคุณสามารถทำได้:

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)จะส่งคืนหนึ่งแบบสอบถามพร้อมตัวกรองทั้งหมดในWHEREประโยค


1

สำหรับห่วง:

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

ลด:

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)

ทั้งสองสิ่งนี้เทียบเท่ากับ Article.objects.filter(pk__in=values)

สิ่งสำคัญคือต้องพิจารณาว่าคุณต้องการอะไรเมื่อvaluesว่างเปล่า คำตอบที่หลายคนที่มีQ()เป็นค่าเริ่มต้นจะกลับมาทุกอย่าง Q(pk__in=[])เป็นค่าเริ่มต้นที่ดีกว่า มันเป็นออบเจ็กต์ Q ที่ล้มเหลวตลอดเวลาซึ่งได้รับการจัดการอย่างดีโดยเครื่องมือเพิ่มประสิทธิภาพ (แม้แต่สมการที่ซับซ้อน)

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

หากคุณต้องการคืนทุกอย่างเมื่อvaluesว่างคุณควรและด้วย~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

มันเป็นสิ่งสำคัญที่ต้องจำว่าQ()เป็นอะไรที่ไม่ประสบความสำเร็จเสมอวัตถุ Q การดำเนินการใด ๆ ที่เกี่ยวข้องกับมันจะทำให้มันหายไปทั้งหมด


0

ง่าย ..
from django.db.models import Q import คุณ model args = (Q (visibility = 1) | (Q (visibility = 0) & Q (user = self.user))) #Tuple parameters = {} #dic order = 'create_at' จำกัด = 10

Models.objects.filter(*args,**parameters).order_by(order)[:limit]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.