繁体   English   中英

使用 Django 中相关 model 中的两个字段的总和来注释查询

[英]Annotating query with sum of multiplication of two fields from a related model in Django

相关模型有:

class Score(models.Model):
    chouette = models.ForeignKey(
        Chouette, on_delete=models.CASCADE, related_name="scores"
    )
    game = models.IntegerField(blank=True, null=True)
    player = models.ForeignKey(User, on_delete=models.CASCADE, related_name="scores")
    position = models.CharField(max_length=10, blank=True, null=True)
    score = models.DecimalField(max_digits=10, decimal_places=3, blank=True, null=True)

    objects = ScoreManager()

class User(AbstractUser):
    name = CharField(_("Name of User"), blank=True, max_length=255)
    handle = CharField(max_length=10)
    xp = IntegerField(default=0)
    objects = CustomUserManager()

我有一个用户自定义管理器,它构建了一个带有很多注释的查询集,但我正在努力解决的问题与分数有关。 我正在尝试计算一个 total_winnings 注释,该注释对每个用户执行子查询,并将与用户关联的每个 Score 实例的 Score 字段乘以 Chouette model 中的字段(未显示 - 只是一个称为赌注的小数字段)。 我已经尝试了几种变体作为我的子查询:

def build_chwinnings(self):
    total_winnings = self.filter(pk=OuterRef("pk")).annotate(
        total_winnings = Sum(F("scores__score") * F("scores__chouette__stake")
        )
    )
    return self.annotate(total_winnings=Subquery(total_winnings.values("total_winnings"))

但它们都导致以下异常:

django.db.utils.ProgrammingError: more than one row returned by a subquery used as an expression

执行此操作的“最简单”方法是将计算值 (scores__score * scores__chouette__stake) 作为字段添加到分数 model,但所有嘘鸟都讨厌在数据库中复制数据。 将其作为属性添加到 Score 或 Score 的查询集不起作用,因为这些不能通过子查询访问。

有没有一种很好的方法可以做到这一点,或者在 Score 中添加一个字段并在每次保存时更新它是否最有意义? (Chouette 中的质押价值通常不会在事后发生变化,所以我不太可能担心任何信号,实际上,Score 实例在创建后同样“固定”。)

您可能在子查询中的 player 列上缺少 group by。 这是一个应该工作的例子。 您可以调整它以在您的 Manager 中工作。

from django.db.models import Subquery, Sum, F, OuterRef

tw_subquery = Subquery(Score.objects.values(
    'player'  # Required to group the annotation by the player
).annotate(
    total_winnings = Sum(F("score") * F("chouette__stake")
).filter(
    player_id=OuterRef('id')
).values(
    'total_winnings'  # required to select only one column
))

users = User.objects.annotate(total_winnings=tw_subquery)

这种子查询由django-sql-utils package为您处理。 这将允许您执行以下操作:

from sql_util import SubquerySum

tw_subquery = SubquerySum(F("scores__score") * F("scores__chouette__stake"))
users = User.objects.annotate(total_winnings=tw_subquery)

作为一个完全独立的问题,在我看来,在您的build_chwinnings方法中

total_winnings = self.filter(...)

是不正确的。 此查询集应在Score model 上,但如果这是User model 的经理,则self将成为User model 上的查询集。 但我不确定我是否看到了足够多的代码来确定这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM