【问题标题】:Annotating a count of a superset of fields with Django使用 Django 注释字段超集的计数
【发布时间】:2020-10-13 22:35:26
【问题描述】:

所以这里的设置是我有一个包含一堆帖子的Post 表。其中一些行是同一帖子的不同版本,它们按post_version_group_id 分组在一起,所以看起来像这样:

pk | title | post_version_group_id
1  | a     | 123
2  | b     | 789
3  | c     | 123
4  | d     | 123

所以有两个“组”帖子,总共 4 个帖子。现在每个帖子都有一个指向PostDownloads 表的外键,看起来像

post | user_downloaded
1    | user1
2    | user2
3    | user3
4    | user4

我想做的是注释我的 Post 查询集,使其看起来像:

pk | title | post_version_group_id | download_count 
1  | a     | 123                   | 3
2  | b     | 789                   | 1
3  | c     | 123                   | 3
4  | d     | 123                   | 3

即具有相同post_version_group_id 的所有帖子具有相同的计数(即不同版本的下载总和)。

目前,我正在做:

Post.objects.all().annotate(download_count=models.Count("downloads__user_downloaded, distinct=True))

这不太行,它注释了一个download_count,看起来像:

pk | title | post_version_group_id | download_count 
1  | a     | 123                   | 1
2  | b     | 789                   | 1
3  | c     | 123                   | 1
4  | d     | 123                   | 1

因为downloads__user_downloaded 似乎仅限于downloads 表中的行集,该表链接到当前的帖子行被注释,这是有道理的 - 真的,但在这种特殊情况下对我不利.

我也尝试过的一件事是

Post.objects.all().values("post_version_group_id").annotate(download_count=Count("downloads__user_downloaded", distinct=True))

哪种方法有效,但.values() 位会破坏查询集和发布实例到字典查询集 - 我需要它来保留发布实例的查询集。

实际模型如下所示:

class Post:
  title = models.CharField()
  post_version_group_id = models.UUIDField()

class PostDownloads:
  post = models.ForeignKey(Post)
  user_downloaded = models.ForeignKey(User)

【问题讨论】:

  • 你能把精确模型添加到OP吗?
  • 实际模型相当大@ArakkalAbu——但我写的伪表应该是模型相关字段的准确视图,让我看看我是否可以扩展一点。

标签: python django postgresql django-models


【解决方案1】:

所以,我最终弄清楚了这一点,并认为我会为其他陷入同样困境的人发布答案。这里的关键是使用Subquery,但不仅仅是任何Subquery——一个返回行数的自定义类型,而不是返回单行数据的默认Subquery类型。

第一步是定义这个自定义子查询类型:

class SubqueryCount(models.Subquery):
    template = "(SELECT count(*) FROM (%(subquery)s) _count)"
    output_field = models.IntegerField()

然后构建子查询:

downloads_subquery = PostDownloads
                     .objects
                     .filter(
                         post__post_version_group_id=models.OuterRef(
                             "post_version_group_id"
                         )
                     )
                     .distinct("user")

根据我拥有的分组版本 ID 进行过滤。

最后,执行注解中的子查询:

Post.objects.annotate(download_count=SubqueryCount(downloads_subquery))

【讨论】:

    猜你喜欢
    • 2016-03-16
    • 2012-10-20
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多