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()
i annotate()
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ę po annotate()
, nawet jeśli nie ma class 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 zmian class 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(
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.