Bagaimana saya dapat menemukan gabungan dari dua queryset Django?

93

Saya punya model Django dengan dua metode manajer ubahsuaian. Masing-masing mengembalikan subset objek model yang berbeda, berdasarkan properti objek yang berbeda.

Apakah ada cara untuk mendapatkan queryset, atau hanya daftar objek, yang merupakan gabungan dari queryset yang dikembalikan oleh setiap metode manajer?

Paul D. Waite
sumber
3
(Dari jawaban yang dihapus) Lihat pertanyaan ini untuk variasi yang berfungsi dengan QuerySets dari Model yang berbeda: stackoverflow.com/questions/431628/…
rnevius
1
Mulai dari versi 1.11, set kueri django memiliki metode gabungan bawaan. Saya telah menambahkannya sebagai jawaban untuk referensi di masa mendatang
Jose Cherian

Jawaban:

179

Ini berfungsi dan terlihat sedikit lebih bersih:

records = query1 | query2

Jika Anda tidak ingin duplikat, maka Anda perlu menambahkan .distinct():

records = (query1 | query2).distinct()
Jordan Reiter
sumber
5
Sementara jawaban yang diterima mengembalikan gabungan yang dapat diulang (daftar tepatnya), seperti yang diminta OP, metode ini mengembalikan gabungan queryset yang sebenarnya. Queryset ini dapat dioperasikan lebih lanjut, yang diinginkan dalam banyak situasi.
Krystian Cybulski
5
Karena bug Django, konstruksi ini terkadang bisa mengembalikan hasil yang salah ketika berhadapan dengan ManyToManyFields. Misalnya, terkadang Anda akan melihat bahwa records.count()akan lebih besar dari query1.count() + query2.count(), yang jelas tidak benar.
Jian
4
@Jian Bisakah Anda mengklarifikasi versi django dengan bug dan link ke masalah djangoproject?
IMFletcher
10
catatan = kueri1 | query2; records = records.distinct () akan memberi saya hasil yang benar
eugene
5
Anda dapat membebani operator dengan Python. Lihat docs.python.org/2/library/operator.html . Jadi apa yang dilakukan Django adalah membuat metode khusus untuk objek QuerySet. Lihat kode di sini: github.com/django/django/blob/master/django/db/models/... yang QuerySetkelas menyediakan metode untuk __and__dan __or__yang dipanggil saat &atau |operator yang digunakan antara dua QuerySetbenda (juga digunakan untuk Qkelas juga ).
Jordan Reiter
49

Mulai dari versi 1.11 , querysets django memiliki metode penyatuan bawaan.

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

Lihat posting blog saya tentang ini untuk lebih banyak contoh.

Jose Cherian
sumber
Saya tidak bisa mendapatkan semua = Benar untuk bekerja. Akhirnya melakukan casting queryset saya ke satu set sebelum mengembalikannya ke klien.
Braden Holt
1
@BradenHolt, all = True, artinya itu akan berisi catatan duplikat. Anda cukup menghapus all = True untuk menghindari mentransmisikannya ke satu set.
Jose Cherian
setelah ini tidak bekerja DjangoFilterBackend, bagaimana saya bisa menggunakan union dan DjangoFilterBackend?
nesalexy
Sayangnya, ini tampaknya tidak berfungsi untuk model dengan urutan default yang ditentukan di Meta model. Setiap kali saya mencoba untuk menggabungkan ini dengan .union, saya menerima kesalahan berikut: "ORDER BY tidak diizinkan di subkueri pernyataan gabungan."
jrial
4

Saya akan menyarankan menggunakan 'query1.union (query2)' daripada 'query1 | query2 '; Saya mendapatkan hasil yang berbeda dari dua metode di atas dan yang pertama adalah yang saya harapkan. Berikut ini adalah apa yang saya temui:

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

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

hasil:

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

| result:
KafkaTopic object
KafkaTopic object
Xianxing
sumber
1
Harap tempel kode, bukan gambar kode. Teks dalam gambar tidak dapat dicari, Anda tidak dapat menyalin / menempelkannya ke editor Anda untuk verifikasi, dan membutuhkan lebih banyak ruang daripada yang diperlukan. Gunakan backticks untuk menandai kode sebagai kode, sehingga diformat dengan benar. Lihat link "bantuan" di sebelah kotak entri teks.
jrial
Terima kasih telah memperbarui. :)
jrial