Saya memiliki model yang terlihat seperti ini:
class Category(models.Model):
name = models.CharField(max_length=60)
class Item(models.Model):
name = models.CharField(max_length=60)
category = models.ForeignKey(Category)
Saya ingin memilih jumlah (hanya hitungan) item untuk setiap kategori, jadi dalam SQL akan sesederhana ini:
select category_id, count(id) from item group by category_id
Apakah ada persamaan dengan melakukan ini "cara Django"? Atau apakah SQL biasa satu-satunya pilihan? Saya akrab dengan metode count () di Django, namun saya tidak melihat bagaimana kelompok oleh akan cocok di sana.
Jawaban:
Di sini, seperti yang baru saya temukan, adalah bagaimana melakukan ini dengan API agregasi Django 1.1:
from django.db.models import Count theanswer = Item.objects.values('category').annotate(Count('category'))
sumber
order_by()
jika'category'
bukan pemesanan default. (Lihat jawaban Daniel yang lebih komprehensif.).annotate()
bekerja sedikit berbeda setelah.values()
: "Namun, ketika klausa values () digunakan untuk membatasi kolom yang dikembalikan dalam kumpulan hasil, metode untuk mengevaluasi anotasi sedikit berbeda. Daripada mengembalikan anotasi hasil untuk setiap hasil di QuerySet asli, hasil asli dikelompokkan sesuai dengan kombinasi unik dari bidang yang ditentukan dalam klausa values (). "( Mutakhirkan : Dukungan agregasi ORM penuh sekarang disertakan dalam Django 1.1 . Sesuai dengan peringatan di bawah ini tentang penggunaan API privat, metode yang didokumentasikan di sini tidak lagi bekerja dalam versi pasca-1.1 dari Django. Saya belum menggali untuk mencari tahu mengapa; jika Anda menggunakan 1.1 atau lebih baru, Anda harus tetap menggunakan API agregasi nyata .)
Dukungan agregasi inti sudah ada di 1.0; itu hanya tidak berdokumen, tidak didukung, dan belum memiliki API ramah di atasnya. Namun, inilah cara Anda tetap menggunakannya hingga versi 1.1 tersedia (dengan risiko Anda sendiri, dan dengan pengetahuan penuh bahwa atribut query.group_by bukan bagian dari API publik dan dapat berubah):
query_set = Item.objects.extra(select={'count': 'count(1)'}, order_by=['-count']).values('count', 'category') query_set.query.group_by = ['category_id']
Jika Anda kemudian mengulangi query_set, setiap nilai yang dikembalikan akan menjadi kamus dengan kunci "kategori" dan kunci "hitungan".
Anda tidak perlu memesan berdasarkan -hitung di sini, itu hanya disertakan untuk mendemonstrasikan cara melakukannya (harus dilakukan dalam panggilan .extra (), bukan di tempat lain dalam rantai konstruksi queryset). Juga, Anda bisa saja mengatakan count (id) daripada count (1), tetapi yang terakhir mungkin lebih efisien.
Perhatikan juga bahwa ketika menyetel .query.group_by, nilainya harus nama kolom DB sebenarnya ('category_id') bukan nama bidang Django ('category'). Ini karena Anda mengutak-atik kueri internal pada tingkat di mana semuanya ada dalam istilah DB, bukan istilah Django.
sumber
Karena saya agak bingung tentang bagaimana pengelompokan dalam Django 1.1 bekerja, saya pikir saya akan menjelaskan di sini tentang bagaimana tepatnya Anda akan menggunakannya. Pertama, ulangi apa yang dikatakan Michael:
Perhatikan juga bahwa Anda perlu
from django.db.models import Count
!Ini hanya akan memilih kategori dan kemudian menambahkan anotasi yang disebut
category__count
. Bergantung pada pengurutan default, ini mungkin yang Anda butuhkan, tetapi jika pengurutan default menggunakan bidang selaincategory
ini tidak akan berfungsi . Alasannya adalah bahwa bidang yang diperlukan untuk memesan juga dipilih dan membuat setiap baris unik, sehingga Anda tidak akan mendapatkan barang yang dikelompokkan sesuai keinginan Anda. Salah satu cara cepat untuk memperbaikinya adalah dengan mengatur ulang pemesanan:Item.objects.values('category').annotate(Count('category')).order_by()
Ini harus menghasilkan hasil yang Anda inginkan. Untuk menyetel nama anotasi, Anda dapat menggunakan:
...annotate(mycount = Count('category'))...
Kemudian Anda akan mendapatkan anotasi
mycount
dalam hasil.Segala sesuatu yang lain tentang pengelompokan sangat mudah bagi saya. Pastikan untuk memeriksa API agregasi Django untuk info lebih rinci.
sumber
Bagaimana dengan ini? (Selain lambat.)
counts= [ (c, Item.filter( category=c.id ).count()) for c in Category.objects.all() ]
Ini memiliki keuntungan karena pendek, bahkan jika itu mengambil banyak baris.
Edit.
Versi satu kueri. BTW, ini seringkali lebih cepat dari SELECT COUNT (*) dalam database. Cobalah untuk melihat.
counts = defaultdict(int) for i in Item.objects.all(): counts[i.category] += 1
sumber