Django:同时使用两个相关字段/外键

问题描述 投票:0回答:1

我有三个型号:

class Team(models.Model):
    team_id = models.AutoField()
    team_name = models.CharField()

class Game(models.Model):
    game_id = models.AutoField()
    home_team = models.ForeignKey(Team, on_delete=models.CASCADE)
    away_team = models.ForeignKey(Team, on_delete=models.CASCADE)

class TeamBoxScore(models.Model):
    game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name='game_boxscore')
    team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='team_boxscore')
    # some stats here

我想要做的是,对于每个 Game 实例,访问与

TeamBoxScore 
实例和
Game 
/
Team 
引用的
home_team
实例相匹配的
away_team

有没有一种方法可以让

game.home_team.team_boxscore
仅参考该
game_id
的评分?或者,相反,我可以指定要返回哪个
game.game_boxscore
(匹配
home_team
的那个还是匹配
away_team
的那个)?我想尽量减少查询并避免长时间的迭代解决方案。

我尝试将每个

TeamBoxScore
实例中的字段注释到每个
Game
(引用
game_boxscore
相关字段),但这最终会使查询集的大小加倍,并放置来自主场得分和客场得分的值单独实例中的 boxcore。我尝试过查看
team_boxscore
相关字段,但这会返回该团队的每个得分 (
game.home_team.team_boxscore
)。

我进一步尝试对这些带注释的值进行分组并仅返回总和,但这既不雅观又耗时太长(并且可能是错误的!)

我尝试在

Prefetch
上使用过滤查询集:

home_team_boxscore_prefetch = Prefetch(
        'home_team__team_boxscore',
        queryset=TeamBoxScore.objects.filter(game_id=F('game__game_id'),   team_id=F('game__home_team__team_id')),
        to_attr='home_team_boxscore'
    )

然后将其传递给

prefetch_related
,但由于某种原因最终会返回 home_team 的所有主场比赛。有任何想法吗?我觉得我缺少一些非常简单的东西。

django nested annotations inner-join django-prefetch-related
1个回答
0
投票

您是否尝试过注释 TeamBoxScore ID?

from django.db.models import Case, When, F, Value, IntegerField

games = Game.objects.filter(**game_filters)

# Annotate each Game instance with the corresponding TeamBoxScore based on home_team or away_team
games_with_boxscores = games.annotate(
    home_team_boxscore_id=Case(
        When(home_team_id=F('home_team__team_id'), then=F('game_boxscore')),
        default=Value(None),
        output_field=IntegerField(),
    ),
    away_team_boxscore_id=Case(
        When(away_team_id=F('away_team__team_id'), then=F('game_boxscore')),
        default=Value(None),
        output_field=IntegerField(),
    )
)

for game in games_with_boxscores:
    home_team_boxscore = TeamBoxScore.objects.filter(id=game.home_team_boxscore_id).first()
    away_team_boxscore = TeamBoxScore.objects.filter(id=game.away_team_boxscore_id).first()

    # Do whatever you need with the boxscores

© www.soinside.com 2019 - 2024. All rights reserved.