python - Django相当于count和group by

我有一个看起来像这样的模型:

class Category(models.Model):
    name = models.CharField(max_length=60)

class Item(models.Model):
    name = models.CharField(max_length=60)
    category = models.ForeignKey(Category)

我想要为每个类别选择项目的计数(只是计数),所以在SQL中它就像这样简单:

select category_id, count(id) from item group by category_id

有没有相当于这样的“Django方式”? 或者纯SQL是唯一的选择吗? 我熟悉Django中的count()方法,但我不知道group by是如何适合的。

Sergey Golovchenko asked 2019-09-10T12:25:51Z
4个解决方案
127 votes

在这里,正如我刚刚发现的,是如何使用Django 1.1聚合API执行此操作:

from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))
michael answered 2019-09-10T12:26:06Z
58 votes

(更新:完整的ORM聚合支持现在包含在Django 1.1中。正如下面关于使用私有API的警告,此处记录的方法不再适用于1.1版本的Django。我没有挖出来找出原因; 如果你在1.1或更高版本,你应该使用真正的聚合API。)

核心聚合支持已经存在于1.0中; 它只是没有文档记录,不受支持,并且还没有友好的API。 但是这里是你如何使用它,直到1.1到达(风险自负,并完全了解query.group_by属性不是公共API的一部分,可能会改变):

query_set = Item.objects.extra(select={'count': 'count(1)'}, 
                               order_by=['-count']).values('count', 'category')
query_set.query.group_by = ['category_id']

如果然后迭代query_set,则每个返回的值将是一个带有“category”键和“count”键的字典。

您不必在此处通过-count进行排序,只是为了演示它是如何完成的(它必须在.extra()调用中完成,而不是在queryset构造链的其他地方完成)。 此外,你也可以说count(id)而不是count(1),但后者可能更有效率。

另请注意,在设置.query.group_by时,值必须是实际的DB列名('category_id')而不是Django字段名称('category')。 这是因为你正在调整查询内部的水平,其中所有内容都是数据库术语,而不是Django术语。

Carl Meyer answered 2019-09-10T12:27:02Z
55 votes

由于我对Django 1.1中的分组工作有点困惑,我想我会详细说明你如何使用它。 首先,重复迈克尔所说的话:

在这里,正如我刚刚发现的,是如何使用Django 1.1聚合API执行此操作:

from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))

另请注意,您需要mycount

这将仅选择类别,然后添加一个名为mycount的注释。根据默认顺序,这可能是您所需要的,但如果默认排序使用mycount以外的字段,则无效。 原因是订购所需的字段也被选中并使每一行都是唯一的,因此您不会按照您想要的方式对其进行分组。 解决此问题的一种快速方法是重置排序:

Item.objects.values('category').annotate(Count('category')).order_by()

这应该产生你想要的结果。 要设置可以使用的注释的名称:

...annotate(mycount = Count('category'))...

然后,您将在结果中有一个名为mycount的注释。

其他关于分组的事情对我来说非常简单。 请务必查看Django聚合API以获取更多详细信息。

Daniel answered 2019-09-10T12:28:09Z
2 votes

这个怎么样? (除了慢。)

counts= [ (c, Item.filter( category=c.id ).count()) for c in Category.objects.all() ]

它具有短小的优点,即使它确实获取了很多行。


编辑。

一个查询版本。 顺便说一句,这通常比数据库中的SELECT COUNT(*)更快。 试试看。

counts = defaultdict(int)
for i in Item.objects.all():
    counts[i.category] += 1
S.Lott answered 2019-09-10T12:28:53Z
translate from https://stackoverflow.com:/questions/327807/django-equivalent-for-count-and-group-by