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

时间:2019-07-29 21:22:30

标签: django django-queryset django-filter

我有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)

我正在尝试获取:

  1. 所有Content不为空的second_content

  2. 用相关的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),但是它也不起作用!

1 个答案:

答案 0 :(得分: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')

请注意,这里您要计算两个单独的JOIN,所以也许应该在distinct=True parameter [Django-doc]中添加一个集Count(..) expression [Django-doc],以避免计算相同的wins和{{1 }}。