Pertimbangkan model Django sederhana Event
dan Participant
:
class Event(models.Model):
title = models.CharField(max_length=100)
class Participant(models.Model):
event = models.ForeignKey(Event, db_index=True)
is_paid = models.BooleanField(default=False, db_index=True)
Sangat mudah untuk membuat anotasi kueri acara dengan jumlah total peserta:
events = Event.objects.all().annotate(participants=models.Count('participant'))
Bagaimana cara membuat anotasi dengan jumlah peserta yang difilter is_paid=True
?
Saya perlu menanyakan semua acara terlepas dari jumlah pesertanya, misalnya, saya tidak perlu memfilter berdasarkan hasil yang dianotasi. Kalau ada 0
peserta tidak apa-apa, saya hanya perlu 0
di annotated value.
The contoh dari dokumentasi tidak bekerja di sini, karena tidak termasuk benda dari query bukannya annotating mereka dengan 0
.
Memperbarui. Django 1.8 mempunyai fitur ekspresi kondisional baru , jadi sekarang kita bisa melakukan seperti ini:
events = Event.objects.all().annotate(paid_participants=models.Sum(
models.Case(
models.When(participant__is_paid=True, then=1),
default=0,
output_field=models.IntegerField()
)))
Pembaruan 2. Django 2.0 mempunyai fitur agregasi Bersyarat baru , lihat jawaban yang diterima di bawah.
aggregate
penggunaan yang ditampilkan. Apakah Anda sudah menguji pertanyaan seperti itu? (Saya belum dan saya ingin percaya! :)Baru saja menemukan bahwa Django 1.8 mempunyai fitur ekspresi kondisional baru , jadi sekarang kita bisa melakukan seperti ini:
sumber
Count
(daripadaSum
) saya rasa kita harus mengaturdefault=None
(jika tidak menggunakanfilter
argumen django 2 ).MEMPERBARUI
Pendekatan sub-kueri yang saya sebutkan sekarang didukung di Django 1.11 melalui sub-ekspresi-subkueri .
Saya lebih suka ini daripada agregasi (jumlah + kasus) , karena itu harus lebih cepat dan lebih mudah untuk dioptimalkan (dengan pengindeksan yang tepat) .
Untuk versi yang lebih lama, hal yang sama dapat dicapai dengan menggunakan
.extra
sumber
.extra
, karena saya lebih suka menghindari SQL di Django :) Saya akan memperbarui pertanyaannya.Django 1.8.2
, jadi saya rasa Anda menggunakan versi itu dan itulah mengapa ini bekerja untuk Anda. Anda dapat membaca lebih lanjut tentang itu di sini dan di siniNone
juga mengerti . Solusi saya adalah menggunakanCoalesce
(from django.db.models.functions import Coalesce
). Anda menggunakannya seperti ini:Coalesce(Subquery(...), 0)
. Mungkin ada pendekatan yang lebih baik.Saya akan menyarankan untuk menggunakan
.values
metodeParticipant
queryset Anda sebagai gantinya.Singkatnya, apa yang ingin Anda lakukan diberikan oleh:
Contoh lengkapnya adalah sebagai berikut:
Buat 2
Event
d:Tambahkan
Participant
ke mereka:Kelompokkan semua
Participant
menurutevent
bidangnya:Di sini diperlukan perbedaan:
Apa
.values
dan yang.distinct
sedang dilakukan di sini adalah bahwa mereka membuat duaParticipant
kelompok yang dikelompokkan berdasarkan elemennyaevent
. Perhatikan bahwa ember itu berisiParticipant
.Anda kemudian dapat memberi anotasi pada keranjang tersebut karena keranjang berisi kumpulan aslinya
Participant
. Di sini kami ingin menghitung jumlahnyaParticipant
, ini hanya dilakukan dengan menghitungid
s dari elemen dalam kotak tersebut (karena itu adalahParticipant
):Terakhir, Anda hanya menginginkan
Participant
denganis_paid
makhlukTrue
, Anda dapat menambahkan filter di depan ekspresi sebelumnya, dan ini menghasilkan ekspresi yang ditunjukkan di atas:Satu-satunya kelemahan adalah Anda harus mengambil
Event
setelahnya karena Anda hanya memilikiid
dari metode di atas.sumber
Hasil apa yang saya cari:
Secara umum, saya harus menggunakan dua kueri berbeda:
Tapi saya ingin keduanya dalam satu kueri. Karenanya:
Hasil:
sumber