Django相当于count和group by

时间:2008-11-29 18:19:28

标签: python django

我的模型看起来像这样:

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 是如何适合的。

4 个答案:

答案 0 :(得分:127)

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

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

答案 1 :(得分:58)

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

答案 2 :(得分:55)

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

  

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

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

另请注意,您需要from django.db.models import Count

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

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

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

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

然后,您将在结果中使用名为mycount的注释。

关于分组的其他所有内容对我来说都非常简单。请务必查看Django aggregation API以获取更多详细信息。

答案 3 :(得分:2)

这是怎么回事? (除了慢。)

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