如何在 Django 中选择计数(*)组按和订购按?

我使用一个事务模型来跟踪通过系统的所有事件

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
114117 次浏览

根据文件,你应使用:

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

Value () : 指定哪些列将用于“分组”

姜戈文档:

”当使用 value ()子句约束 返回的结果集中,评估注释的方法是 而不是返回一个带注释的结果 结果,则对原始结果进行分组 控件中指定的字段的唯一组合 价值()条款”

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

姜戈文档:

生成摘要值的第二种方法是为 QuerySet 中的每个对象生成一个独立的摘要。例如,如果你 正在检索一个图书列表,你可能想知道有多少作者 每本书都有一个多对多的关系 我们想为每本书总结一下这种关系 在查询集中。

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

从句顺序是不言自明的。

总结一下: 通过生成一个作者查询集,添加注释(这将为返回的值添加额外的字段) ,最后按这个值对它们进行排序

请参考 https://docs.djangoproject.com/en/dev/topics/db/aggregation/了解更多内容

值得注意的是: 如果使用 Count,传递给 Count 的值不会影响聚合,只会影响最终值的名称。聚合器按值的唯一组合(如上所述)进行分组,而不按传递给 Count 的值进行分组。下列查询是相同的:

Transaction.objects.all().values('actor').annotate(total=Count('actor')).order_by('total')
Transaction.objects.all().values('actor').annotate(total=Count('id')).order_by('total')

就像@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()子句对于正确的结果是必需的。即使没有订购的意图,你也不能跳过它。

此外,对于高质量的代码,建议始终在 annotate()之后放置一个 .order_by()子句,即使没有 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 在 class Meta: ordering的每个字段上执行额外的 GROUP BY

如果您要打印查询:

>>> 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 官方文档中的 与默认排序或 order _ by ()的交互

如果你想反向(较大的价值较小的价值)订单只是使用 -减。

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