使用 Django 中相关模型的两个字段的乘法总和来注释查询
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 注释,该注释对每个用户执行子查询,并通过 Chouette 模型中的字段(未显示 - 只是一个小数字段)将与用户关联的每个 Score 实例的 Score 中的分数字段相乘称为桩)。我已经尝试了几个变体作为我的子查询:
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) 作为一个字段添加到 Score 模型中,但所有 boo birds 都讨厌在其中重复数据数据库。将它作为 属性 添加到 Score 或 Score 的查询集中不起作用,因为它们无法通过子查询访问。
有没有一个很好的方法来做到这一点,或者在 Score 中添加一个字段并在每次保存时更新它是否最有意义? (Chouette 中的赌注值通常不会在事后发生变化,因此我不太可能需要担心任何信号,实际上,Score 实例在创建后同样是“固定的”。)
您可能在子查询的玩家列中遗漏了分组依据。这是一个应该起作用的例子。您可以调整它以在您的管理器中工作。
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
模型上,但是如果这是 User
模型的管理器,self
将成为 User
模型上的查询集。但我不确定我看到的代码是否足够确定这一点。
相关型号为:
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 注释,该注释对每个用户执行子查询,并通过 Chouette 模型中的字段(未显示 - 只是一个小数字段)将与用户关联的每个 Score 实例的 Score 中的分数字段相乘称为桩)。我已经尝试了几个变体作为我的子查询:
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) 作为一个字段添加到 Score 模型中,但所有 boo birds 都讨厌在其中重复数据数据库。将它作为 属性 添加到 Score 或 Score 的查询集中不起作用,因为它们无法通过子查询访问。
有没有一个很好的方法来做到这一点,或者在 Score 中添加一个字段并在每次保存时更新它是否最有意义? (Chouette 中的赌注值通常不会在事后发生变化,因此我不太可能需要担心任何信号,实际上,Score 实例在创建后同样是“固定的”。)
您可能在子查询的玩家列中遗漏了分组依据。这是一个应该起作用的例子。您可以调整它以在您的管理器中工作。
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
模型上,但是如果这是 User
模型的管理器,self
将成为 User
模型上的查询集。但我不确定我看到的代码是否足够确定这一点。