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) 

我想每个类别的项目select计数(只是计数),所以在SQL中,它将如此简单:

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

有没有相当于做这个“Django的方式”? 或者是纯SQL是唯一的select? 我熟悉Django中的count()方法,但是我没有看到如何通过组合。

在这里,我刚刚发现,是如何用Django 1.1聚合API做到这一点的:

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

更新 :完整的ORM聚合支持现在包含在Django 1.1中, 正如下面有关使用私有API的警告,这里logging的方法不再适用于Django的后1.1版本,我还没有find原因。如果你在1.1或者更高版本,你应该使用真正的聚合API 。)

核心聚合支持已经在1.0; 它只是没有logging,不受支持,并没有一个友好的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,每个返回的值将是一个带有“类别”键和“计数”键的字典。

您不必在此处使用-count进行sorting,只是为了演示如何完成(必须在.extra()调用中完成,而不是在queryset构build链中的其他位置)。 此外,你可以说count(id)而不是count(1),但后者可能更有效率。

还要注意,在设置.query.group_by时,这些值必须是实际的DB列名('category_id')而不是Django字段名('category')。 这是因为你正在调整查询内部的一切在数据库方面,而不是Django的条款。

由于我对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

这将只select类别,然后添加名为category__count的注释。 根据默认的顺序,这可能是你所需要的, 但是如果默认顺序使用的是非category字段,这将不起作用 。 原因是订购所需的字段也被选中,使每一行都是唯一的,所以你不会得到你想要的东西。 解决这个问题的一个快速方法是重置sorting:

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

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

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

然后你会在结果中有一个名为mycount的注解。

关于分组的其他一切都是非常简单的。 请务必查看Django聚合API以获取更详细的信息。

这个怎么样? (慢)。

 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