ฉันจะหาการรวมกันของชุดแบบสอบถาม Django สองชุดได้อย่างไร


93

ฉันมีโมเดล Django ที่มีวิธีจัดการแบบกำหนดเองสองวิธี แต่ละรายการส่งคืนส่วนย่อยที่แตกต่างกันของอ็อบเจ็กต์ของโมเดลตามคุณสมบัติที่แตกต่างกันของอ็อบเจ็กต์

มีวิธีใดบ้างในการรับ queryset หรือเพียงแค่รายการของวัตถุนั่นคือการรวมกันของ querysets ที่ส่งคืนโดยแต่ละวิธีของผู้จัดการ


3
(จากคำตอบที่ถูกลบ) ดูคำถามนี้สำหรับรูปแบบที่ใช้งานได้กับ QuerySets จากโมเดลต่างๆ: stackoverflow.com/questions/431628/…
rnevius

1
ตั้งแต่เวอร์ชัน 1.11 ชุดแบบสอบถาม django มีวิธีการรวมในตัว ฉันได้เพิ่มไว้เป็นคำตอบสำหรับการอ้างอิงในอนาคต
Jose Cherian

คำตอบ:


179

วิธีนี้ใช้งานได้และดูสะอาดขึ้นเล็กน้อย:

records = query1 | query2

หากคุณไม่ต้องการให้ซ้ำกันคุณจะต้องต่อท้าย.distinct():

records = (query1 | query2).distinct()

5
ในขณะที่คำตอบที่ยอมรับจะคืนค่าการรวมซ้ำได้ (รายการต้องถูกต้อง) เช่นเดียวกับที่ OP ถามวิธีนี้จะส่งคืนการรวมกันของชุดคำถามที่แท้จริง ชุดข้อมูลนี้สามารถดำเนินการต่อไปได้ซึ่งเป็นที่ต้องการในหลาย ๆ สถานการณ์
Krystian Cybulski

5
เนื่องจากข้อผิดพลาด Django บางครั้งโครงสร้างนี้อาจส่งคืนผลลัพธ์ที่ไม่ถูกต้องเมื่อจัดการกับManyToManyFields ตัวอย่างเช่นบางครั้งคุณจะเห็นว่าrecords.count()จะมากกว่าquery1.count() + query2.count()ซึ่งไม่ถูกต้องอย่างชัดเจน
Jian

4
@Jian คุณสามารถชี้แจงเวอร์ชั่น django พร้อมกับบั๊กและลิงค์ไปยังปัญหา djangoproject ได้หรือไม่?
IMFletcher

10
บันทึก = query1 | แบบสอบถาม 2; records = records.distinct () จะให้ผลลัพธ์ที่ถูกต้อง
eugene

5
คุณสามารถโอเวอร์โหลดตัวดำเนินการใน Python ดูdocs.python.org/2/library/operator.html ดังนั้นสิ่งที่ Django ทำคือสร้างเมธอดพิเศษสำหรับวัตถุ QuerySet ดูรหัสที่นี่: github.com/django/django/blob/master/django/db/models/...QuerySetระดับให้วิธีการ__and__และ__or__ที่เรียกว่าเมื่อ&หรือ|ผู้ประกอบการจะใช้ระหว่างสองQuerySetวัตถุ (ใช้สำหรับQชั้นเรียนได้เป็นอย่างดี ).
Jordan Reiter

49

เริ่มตั้งแต่เวอร์ชัน 1.11 django querysets มีวิธีการรวมในตัว

q = q1.union(q2) #q will contain all unique records of q1 + q2
q = q1.union(q2, all=True) #q will contain all records of q1 + q2 including duplicates
q = q1.union(q2,q3) # more than 2 queryset union

ดูโพสต์บล็อกของฉันเกี่ยวกับเรื่องนี้สำหรับตัวอย่างเพิ่มเติม


ฉันไม่สามารถรับทั้งหมด = ทำงานได้จริง จบลงด้วยการส่งแบบสอบถามของฉันเป็นชุดก่อนที่จะส่งคืนให้กับลูกค้า
Braden Holt

1
@BradenHolt ทั้งหมด = True หมายความว่าจะมีระเบียนที่ซ้ำกัน คุณสามารถลบ all = True เพื่อหลีกเลี่ยงการแคสต์เป็นชุด
Jose Cherian

หลังจากนี้ไม่ทำงาน DjangoFilterBackend ฉันจะใช้ union และ DjangoFilterBackend ได้อย่างไร
nesalexy

น่าเสียดายที่ดูเหมือนจะใช้ไม่ได้กับโมเดลที่มีลำดับเริ่มต้นที่กำหนดไว้ใน Meta ของโมเดล เมื่อใดก็ตามที่ฉันพยายามรวมสิ่งเหล่านี้กับ. ยูเนี่ยนฉันได้รับข้อผิดพลาดต่อไปนี้: "ORDER BY not allowed in subqueries of compound statement"
jrial

4

ฉันขอแนะนำให้ใช้ 'query1.union (query2)' แทน 'query1 | แบบสอบถาม 2 '; ฉันได้ผลลัพธ์ที่แตกต่างจากสองวิธีข้างต้นและวิธีเดิมคือสิ่งที่ฉันคาดหวัง ต่อไปนี้คือสิ่งที่ฉันเจอ:

print "union result:"
for element in query_set1.union(query_set2):
    print element

print "| result:"
for element in (query_set1 | query_set2):
    print element

ผลลัพธ์:

union result:
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object

| result:
KafkaTopic object
KafkaTopic object

1
กรุณาวางโค้ดไม่ใช่รูปภาพของโค้ด ข้อความในรูปภาพไม่สามารถค้นหาได้คุณไม่สามารถคัดลอก / วางลงในเครื่องมือแก้ไขเพื่อการตรวจสอบได้และใช้พื้นที่มากเกินความจำเป็น ใช้ backticks เพื่อทำเครื่องหมายรหัสเป็นรหัสเพื่อให้ได้รูปแบบที่ถูกต้อง ดูลิงก์ "ความช่วยเหลือ" ถัดจากช่องป้อนข้อความ
jrial

ขอบคุณสำหรับการอัปเดต :)
jrial
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.