如何在Django中执行SELECT COUNT(*)GROUP BY和ORDER BY?

我正在使用事务模型来跟踪系统中的所有事件

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() ...... 

我如何获得系统中的前五名演员?

在SQL中,它将基本上是

 SELECT actor, COUNT(*) as total FROM Transaction GROUP BY actor ORDER BY total DESC 

根据文件,你应该使用:

 from django.db.models import Count Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total') 

values():指定哪些列将被用来“分组”

Django文档:

“当使用values()子句来限制结果集中返回的列时,评估注释的方法略有不同,原始结果不是返回原始QuerySet中每个结果的注释结果,而是根据到values()子句中指定的字段的唯一组合“

annotate():指定对分组值的操作

Django文档:

生成摘要值的第二种方法是为QuerySet中的每个对象生成一个独立的摘要。 例如,如果您正在检索书籍列表,您可能想知道有多less作者对每本书作出贡献。 每本书与作者有多对多的关系; 我们想在QuerySet中总结每个书籍的这种关系。

每个对象的摘要可以使用annotate()子句生成。 当指定了annotate()子句时,QuerySet中的每个对象都将使用指定的值进行注释。

从句的顺序是自我解释的。

总结一下,你们分组,生成一个作者的查询集,添加注释(这将为返回的值添加一个额外的字段),最后,你通过这个值

有关更多信息,请参阅https://docs.djangoproject.com/en/dev/topics/db/aggregation/

就像@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()子句对于正确的结果是强制性的。 即使没有预定的顺序,你也不能跳过它。

此外,对于高质量的代码,build议在annotate()之后始终放置一个.order_by()子句,即使没有class Meta: ordering 。 这种方法将使得声明具有前瞻性:它将按预期工作,而不pipe将来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在class Meta: ordering每个字段上执行额外的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正式文档中的默认sorting或order_by()的交互 。