对中间模型 django 的一个字段进行注释

Posted

技术标签:

【中文标题】对中间模型 django 的一个字段进行注释【英文标题】:Annotate over a field of intermediate model django 【发布时间】:2018-12-04 22:19:11 【问题描述】:

我有以下方案:

class User(AbstractUser):
    pass

class Task(models.Model):
    pass

class Contest(models.Model):
    tasks = models.ManyToManyField('Task',
                               related_name='contests',
                               blank=True,
                               through='ContestTaskRelationship')

    participants = models.ManyToManyField('User',
                                      related_name='contests_participated',
                                      blank=True,
                                      through='ContestParticipantRelationship')

class ContestTaskRelationship(models.Model):
    contest = models.ForeignKey('Contest', on_delete=models.CASCADE)
    task = models.ForeignKey('Task', on_delete=models.CASCADE)
    cost = models.IntegerField()


class ContestParticipantRelationship(models.Model):
    contest = models.ForeignKey('Contest', on_delete=models.CASCADE)
    user = models.ForeignKey('User', on_delete=models.CASCADE)
    task = models.ForeignKey('Task', on_delete=models.CASCADE, related_name='contests_participants_relationship')
    is_solved = models.BooleanField()

现在我得到了contest 对象,需要获取tasks 字段上的所有任务,用用户数注释的rach 解决了它。所以,我需要计算ContestParticipantRelationship 的数量,需要task,需要contestis_solved 设置为True。如何进行这样的查询?

【问题讨论】:

User 可以解决一个任务多次次吗? @WillemVanOnsem,不 【参考方案1】:

大概是这样的:

from django.db.models import IntegerField, Value, Sum
from django.db.models.functions import Cast, Coalesce


Task.objects.filter(
    contests__contest=some_contest,
).annotate(
    nsolved=Cast(Coalesce(
        Sum('contests_participants_relationship__is_solved'), Value(0)
    ),IntegerField())
)

所以这里我们首先过滤任务的竞赛是some_contest。接下来我们在is_solved 列上执行Sum(..)。由于存在可能是NULL 的极端情况(如果没有用户进行尝试等),那么我们将其转换为0,此外我们将其转换为IntegerField,因为否则某些实例可能会使用TrueFalse 进行注释,以防零个或一个用户解决它。

【讨论】:

它不会遍历比赛的所有参与者,对布尔值求和吗?我一直在寻找一种通过过滤然后计数的解决方案 @NikitinRoman:好吧,因为每个用户最多只能解决一次,因此对于每个用户,最多有一个布尔值设置为 true。过滤计数方法将销毁没有用户设法解决的所有任务。所以0s 不会出现在结果中。 并且在 SQL 中根本不存在布尔值:TRUE1 的别名,FALSE0 的别名。在 Python 中,布尔值确实存在,但它们是整数的子类(同样有 True-1/False-0 类比)。 我的意思是,如果在聚合之前进行一些过滤,会不会更快? @NikitinRoman:这取决于过滤。通常如果没有索引存在。过滤应该更慢,因为这会破坏链接,从而导致显着的减速。但就像说的那样,这取决于索引是否到位,因为根据索引的不同,计数根本不属于枚举。

以上是关于对中间模型 django 的一个字段进行注释的主要内容,如果未能解决你的问题,请参考以下文章

Django:将价格注释到行,然后按字段对行进行分组,并将价格总和注释到组

Django:通过注释字段的总和订购查询集?

Django 在模型字段上注释 Avg

Django - 查询:使用相关模型字段注释查询集

Django 在 json 字段中注释特定键

Django 使用某些字段的最新值注释查询集