我正在使用交易模型来跟踪通过系统的所有事件
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()
......
如何在我的系统中获得前5名演员?
在sql中它基本上是
SELECT actor, COUNT(*) as total
FROM Transaction
GROUP BY actor
ORDER BY total DESC
答案 0 :(得分:124)
根据文档,您应该使用:
from django.db.models import Count
Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
values():指定将使用哪些列"按"
分组Django docs:
"当使用values()子句约束列时 在结果集中返回,评估注释的方法是 稍微不一样。而不是为每个返回带注释的结果 结果在原始QuerySet中,原始结果被分组 根据中指定的字段的唯一组合 values()子句"
annotate():指定对分组值
的操作Django docs:
生成汇总值的第二种方法是为QuerySet中的每个对象生成一个独立的摘要。例如,如果你 正在检索书籍列表,您可能想知道有多少作者 为每本书做出了贡献。每本书都有多对多的关系 与作者;我们想要总结每本书的这种关系 在QuerySet中。
可以使用annotate()子句生成每个对象的摘要。 当指定annotate()子句时,QuerySet中的每个对象 将使用指定的值进行注释。
order by子句是不言自明的。
总结一下:你分组,生成一个作者的查询集,添加注释(这将为返回的值添加一个额外的字段),最后,你按这个值排序
有关更多见解,请参阅https://docs.djangoproject.com/en/dev/topics/db/aggregation/
答案 1 :(得分:21)
就像@Alvaro回答了Django对GROUP BY
语句的直接等价:
SELECT actor, COUNT(*) AS total
FROM Transaction
GROUP BY actor
通过使用values()
和annotate()
方法,如下所示:
Transaction.objects.values('actor').annotate(total=Count('actor')).order_by()
如果模型具有class Meta
中定义的默认排序,则.order_by()
子句必须具有正确的结果。即使没有预定排序,也不能跳过它。
此外,对于高质量的代码,建议在.order_by()
之后始终放置annotate()
子句,即使没有class Meta: ordering
。这种方法将使声明具有前瞻性:无论将来对class Meta: ordering
进行任何更改,它都将按预期工作。
让我举个例子。如果模型有:
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']
然后这种方法不起作用:
Transaction.objects.values('actor').annotate(total=Count('actor'))
那是因为Django在GROUP BY
class Meta: ordering
如果要打印查询:
>>> 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"
很明显,聚合不会按预期工作,因此必须使用.order_by()
子句来清除此行为并获得正确的聚合结果。
请参阅Django官方文档中的Interaction with default ordering or order_by()。