Używam modelu transakcyjnego, aby śledzić wszystkie zdarzenia przechodzące przez system
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()
......
jak zdobyć 5 najlepszych aktorów w moim systemie?
W sql w zasadzie będzie
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
django
django-queryset
totoromeow
źródło
źródło
Odpowiedzi:
Zgodnie z dokumentacją należy użyć:
from django.db.models import Count Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
wartości (): określa, które kolumny będą używane do „grupowania według”
Dokumentacja Django:
annotate (): określa operację na zgrupowanych wartościach
Dokumentacja Django:
Kolejność według klauzuli nie wymaga wyjaśnień.
Podsumowując: grupujesz według, generujesz zestaw zapytań autorów, dodajesz adnotację (doda to dodatkowe pole do zwracanych wartości) i na koniec porządkujesz ich według tej wartości
Patrz https://docs.djangoproject.com/en/dev/topics/db/aggregation/ więcej wglądu
Warto zauważyć: jeśli używasz funkcji Count, wartość przekazana do Count nie wpływa na agregację, a jedynie nazwę nadaną wartości końcowej. Agregator grupuje według unikalnych kombinacji wartości (jak wspomniano powyżej), a nie według wartości przekazanej do Count. Następujące zapytania są takie same:
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total') Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')
źródło
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
, nie zapomnij zaimportować Count z django.db.models. DziękujęCount
(i być może innych agregatorów), wartość przekazana doCount
nie wpływa na agregację, a jedynie nazwę nadaną wartości końcowej. Agregator grupuje według unikalnych kombinacjivalues
(jak wspomniano powyżej), a nie według wartości przekazanej doCount
.total
podano nazwę i liczbę użytą w sql,COUNT('actor')
która w tym przypadku nie ma znaczenia, ale jeśli np.values('x', 'y').annotate(count=Count('x'))
DostanieszCOUNT(x)
, nieCOUNT(*)
lubCOUNT(x, y)
właśnie wypróbowałeś./manage.py shell
Tak jak @Alvaro odpowiedział na bezpośredni odpowiednik
GROUP BY
stwierdzenia Django :SELECT actor, COUNT(*) AS total FROM Transaction GROUP BY actor
jest dzięki wykorzystaniu
values()
iannotate()
metody w następujący sposób:Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
Należy jednak zwrócić uwagę na jeszcze jedną rzecz:
Jeżeli model ma zdefiniowaną domyślną kolejność
class Meta
,.order_by()
klauzula jest obligatoryjna dla poprawnych wyników. Po prostu nie można go pominąć, nawet jeśli nie jest planowane zamówienie.Ponadto w przypadku kodu wysokiej jakości zaleca się zawsze umieszczać
.order_by()
klauzulę poannotate()
, nawet jeśli nie maclass Meta: ordering
. Takie podejście sprawi, że oświadczenie będzie przyszłościowe: będzie działać zgodnie z zamierzeniami, niezależnie od przyszłych zmianclass Meta: ordering
.Podam przykład. Gdyby model miał:
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']
Wtedy takie podejście NIE BĘDZIE działało:
Transaction.objects.values('actor').annotate(total=Count('actor'))
To dlatego, że Django wykonuje dodatkowe operacje
GROUP BY
na każdym polu wclass Meta: ordering
Jeśli chcesz wydrukować zapytanie:
>>> 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"
Będzie jasne, że agregacja NIE zadziała zgodnie z przeznaczeniem i dlatego
.order_by()
należy użyć klauzuli, aby usunąć to zachowanie i uzyskać prawidłowe wyniki agregacji.Zobacz: Interakcja z domyślnym zamówieniem lub order_by () w oficjalnej dokumentacji Django.
źródło
.order_by()
uratował mnie przedordering
Meta.