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