Tout comme @Alvaro a répondu à l'équivalent direct de Django pour l'instruction GROUP BY
:
SELECT actor, COUNT(*) AS total
FROM Transaction
GROUP BY actor
est à travers l'utilisation des méthodes values()
et annotate()
comme suit:
Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
Cependant, une autre chose doit être soulignée:
Si le modèle a un tri par défaut défini dans class Meta
, la clause .order_by()
est obligatoire pour obtenir des résultats corrects. Vous ne pouvez pas la sauter même si aucun tri n'est prévu.
De plus, pour un code de haute qualité, il est conseillé de toujours mettre une clause .order_by()
après annotate()
, même s'il n'y a pas de class Meta: ordering
. Cette approche rendra la déclaration pérenne : elle fonctionnera comme prévu, quelles que soient les modifications futures apportées à class Meta: ordering
.
Laissez-moi vous fournir un exemple. Si le modèle avait:
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']
Alors une telle approche NE FONCTIONNERAIT PAS:
Transaction.objects.values('actor').annotate(total=Count('actor'))
C'est parce que Django effectue un GROUP BY
supplémentaire sur chaque champ dans class Meta: ordering
Si vous affichez la requête:
>>> 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"
Il sera clair que l'agrégation ne fonctionnerait PAS comme prévu et donc la clause .order_by()
doit être utilisée pour clarifier ce comportement et obtenir des résultats d'agrégation appropriés.
Voir: Interaction with default ordering or order_by() dans la documentation officielle de Django.