Django与条件聚合

4 投票
3 回答
2161 浏览
提问于 2025-04-15 22:56

我有两个模型,一个是作者(authors),另一个是文章(articles):

class Author(models.Model):
    name = models.CharField('name', max_length=100)

class Article(models.Model)
    title = models.CharField('title', max_length=100)
    pubdate = models.DateTimeField('publication date')
    authors = models.ManyToManyField(Author)

现在我想要选出所有的作者,并且给他们加上各自发表文章的数量。这在Django中用聚合函数来做非常简单。不过问题是,我只想统计那些已经发布的文章。根据Django的一个问题追踪记录ticket 11305,目前还做不到这一点。我尝试使用那个记录中提到的CountIf注释,但它没有正确处理日期时间字符串,也没有进行所有需要的连接操作。

那么,除了写自定义的SQL代码,还有什么好的解决办法呢?

3 个回答

1

我通过创建一个SQL视图来解决了我的问题,这个视图里包含了需要的GROUP BY语句。然后我为这个视图建立了一个模型,并设置了managed = False,同时还添加了一个指向Author表的OneToOneField。虽然这不是最有效或最优雅的解决方案,但它能正常工作。

5

你可以使用这个叫做 django-aggregate-if 的应用,它的灵感来源于 11305号问题。或者,你也可以直接使用 extra 方法来处理查询集(假设你的应用叫“articles”):

Author.objects.all().extra(
    select={'article_count': 'SELECT COUNT(*) FROM "articles_article" '
                             'INNER JOIN "articles_article_authors" '
                             'ON "articles_article"."id" = '
                             '   "articles_article_authors"."article_id" '
                             'WHERE "articles_article_authors"."author_id" = '
                             '      "articles_author"."id" '
                             'AND "articles_article"."pubdate" IS NOT NULL'})
4

Django 1.8及以上版本的解决方案

从Django 1.8开始,条件表达式可以用来构建查询集。

想了解更多细节可以查看文档,不过针对你的问题,快速的解决方案大概是这样的:

today = datetime.date.today()
authors = Author.objects.all().annotate(article_count=Sum(
    Case(When(articles__pubdate__lt=today, then=1),
         output_field=IntegerField())
))

我没有实际测试过,但应该是可以用的。

撰写回答