Django 查询 - 在带注释的计数过滤器中获取父对象

Posted

技术标签:

【中文标题】Django 查询 - 在带注释的计数过滤器中获取父对象【英文标题】:Django query - get parent object in annotated count filter 【发布时间】:2019-12-07 06:01:36 【问题描述】:

我有 3 个模型:

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(db_index=True, max_length=20, unique = True)

class Content(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True) #User.content_set.all() returns all Content objects of the content
    contentURL = models.CharField(max_length=256, null=True)
    thumbnailURL = models.CharField(max_length=256, null=True, blank=True)
    second_content = models.OneToOneField('self', on_delete=models.SET_NULL, null=True, blank=True) #if this is not NULL, then the content has been uploaded with a second one and they form a pair to be retrieved together
    timestamp = models.DateTimeField(auto_now_add=True)

class Fight(models.Model):
    win_content = models.ForeignKey(Content, db_index=True, on_delete=models.SET_NULL, related_name="wins", null=True) #Content.wins.all() returns all Fight objects of the content in which this content has won
    loss_content = models.ForeignKey(Content, db_index=True, on_delete=models.SET_NULL, related_name="losses", null=True) #Content.losses.all() returns all Fight objects of the content in which this content has lost
    user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
    is_private = models.BooleanField(db_index=True, default=False) #we will filter those out for user quality calculations
    timestamp = models.DateTimeField(auto_now_add=True)

我想得到:

    second_content 不为空的所有Content

    用相关的Fight计数注释每个内容:一次是胜利,一次是失败。

这是我的查询集:

contents = user.content_set.exclude(content__second_content=None).annotate(
    win_count=Count('wins', filter=Q(wins__loss_content=second_content)),
    loss_count=Count('losses', filter=Q(losses__win_content=second_content))
).order_by('-timestamp')

问题在于Q(wins__loss_content=second_content)second_content 未定义,因为它引用 Fight 对象,而不是父对象。 如何引用父对象?我试过Q(wins__loss_content=content__second_content),还是不行!

【问题讨论】:

你可以在这里使用F-object,所以Q(wins__loss_content=F('content__second_content')) 嗯,这个简单的答案正是我所需要的!谢谢! 【参考方案1】:

您可以使用F object [Django-doc] 来引用一个字段,例如F('content') 因此引用外键字段。

因此,您可以在如下表达式中使用它:

contents = user.content_set.exclude(content__second_content=None).annotate(
    win_count=Count('wins', filter=Q(wins__loss_content=F('content'))),
    loss_count=Count('losses', filter=Q(losses__win_content=F('content')))
).order_by('-timestamp')

请注意,这里您计算两个单独的JOINs,所以也许您应该在您的Count(..) expression [Django-doc] 中添加一个distinct=True parameter [Django-doc] 以避免将相同的winslosses 计算两次。

【讨论】:

不知何故,我与引用的对象混淆了。我最终得到的正确查询是:contents = user.content_set.exclude(content__second_content=None).annotate( win_count=Count('wins', filter=Q(wins__loss_content=F('content')), distinct=True), loss_count=Count('losses', filter=Q(losses__win_content=F('content')), distinct=True) ) Btw thnx 对于不同的提示,如果没有它确实无法正常工作!

以上是关于Django 查询 - 在带注释的计数过滤器中获取父对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在带注释的 Django 查询集结果上列出字段值?

在带注释的 FilteredRelation 上使用 exclude 不起作用?

如何在 Django 中过滤对象以进行计数注释?

queryset,union,order_by 在带注释的字段上

注释不同的 Django 查询集不再使用不同的查询集

获取子查询的计数