Saya menggunakan model transaksi untuk melacak semua peristiwa yang terjadi melalui sistem
class Transaction(models.Model):
actor = models.ForeignKey(User, related_name="actor")
acted = models.ForeignKey(User, related_name="acted", null=True, blank=True)
action_id = models.IntegerField()
......
bagaimana cara mendapatkan 5 aktor teratas di sistem saya?
Di sql pada dasarnya akan
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
django
django-queryset
totoromeow
sumber
sumber
Jawaban:
Menurut dokumentasi, Anda harus menggunakan:
from django.db.models import Count Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
values (): menentukan kolom mana yang akan digunakan untuk "group by"
Django dokumen:
annotate (): menentukan operasi atas nilai yang dikelompokkan
Django dokumen:
Urutan menurut klausul sudah cukup jelas.
Untuk meringkas: Anda mengelompokkan berdasarkan, menghasilkan queryset penulis, menambahkan anotasi (ini akan menambahkan bidang ekstra ke nilai yang dikembalikan) dan terakhir, Anda mengurutkan mereka dengan nilai ini
Lihat https://docs.djangoproject.com/en/dev/topics/db/aggregation/ untuk wawasan lebih lanjut
Sebaiknya diperhatikan: jika menggunakan Hitung, nilai yang diteruskan ke Hitung tidak memengaruhi agregasi, hanya nama yang diberikan ke nilai akhir. Agregator mengelompokkan berdasarkan kombinasi unik dari nilai-nilai (seperti yang disebutkan di atas), bukan berdasarkan nilai yang diteruskan ke Hitung. Kueri berikut ini sama:
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total') Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')
sumber
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
, jangan lupa untuk mengimpor Hitungan dari django.db.models. Terima kasihCount
(dan mungkin agregator lain), nilai yang diteruskan keCount
tidak memengaruhi agregasi, hanya nama yang diberikan ke nilai akhir. Agregator mengelompokkan berdasarkan kombinasi unikvalues
(seperti yang disebutkan di atas), bukan berdasarkan nilai yang diteruskanCount
.total
adalah nama yang diberikan dan jumlah yang digunakan dalam sql adalahCOUNT('actor')
yang dalam hal ini tidak masalah, tetapi jika misalnyavalues('x', 'y').annotate(count=Count('x'))
, Anda akan mendapatkanCOUNT(x)
, tidakCOUNT(*)
atauCOUNT(x, y)
, hanya mencobanya di./manage.py shell
Seperti @Alvaro telah menjawab padanan langsung Django untuk
GROUP BY
pernyataan:SELECT actor, COUNT(*) AS total FROM Transaction GROUP BY actor
adalah melalui penggunaan
values()
danannotate()
metode sebagai berikut:Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
Namun satu hal lagi yang harus diperhatikan:
Jika model memiliki pengurutan default yang ditentukan
class Meta
,.order_by()
klausul wajib untuk hasil yang sesuai. Anda tidak bisa melewatkannya bahkan ketika tidak ada tujuan pemesanan.Selanjutnya, untuk kode kualitas tinggi, disarankan untuk selalu meletakkan
.order_by()
klausul setelahnyaannotate()
, bahkan jika tidak adaclass Meta: ordering
. Pendekatan seperti itu akan membuat pernyataan tahan di masa depan: itu akan berfungsi seperti yang dimaksudkan, terlepas dari perubahan apa pun di masa mendatangclass Meta: ordering
.Izinkan saya memberi Anda sebuah contoh. Jika model memiliki:
class Transaction(models.Model): actor = models.ForeignKey(User, related_name="actor") acted = models.ForeignKey(User, related_name="acted", null=True, blank=True) action_id = models.IntegerField() class Meta: ordering = ['id']
Maka pendekatan seperti itu TIDAK AKAN berhasil:
Transaction.objects.values('actor').annotate(total=Count('actor'))
Itu karena Django melakukan tambahan
GROUP BY
pada setiap bidang diclass Meta: ordering
Jika Anda ingin mencetak kueri:
>>> print Transaction.objects.values('actor').annotate(total=Count('actor')).query SELECT "Transaction"."actor_id", COUNT("Transaction"."actor_id") AS "total" FROM "Transaction" GROUP BY "Transaction"."actor_id", "Transaction"."id"
Akan jelas bahwa agregasi TIDAK akan berfungsi sebagaimana mestinya dan oleh karena itu
.order_by()
klausul harus digunakan untuk menghapus perilaku ini dan mendapatkan hasil agregasi yang sesuai.Lihat: Interaksi dengan pengurutan default atau order_by () dalam dokumentasi resmi Django.
sumber
.order_by()
menyelamatkan saya dariordering
di Meta.