【问题标题】:Annotate over a field of intermediate model django对中间模型 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,不

标签: django many-to-many django-queryset


【解决方案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:这取决于过滤。通常如果没有索引存在。过滤应该更慢,因为这会破坏链接,从而导致显着的减速。但就像说的那样,这取决于索引是否到位,因为根据索引的不同,计数根本不属于枚举。
猜你喜欢
  • 2021-10-09
  • 2021-01-31
  • 1970-01-01
  • 2013-02-25
  • 2011-05-05
  • 2018-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多