簡體   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