Django: одновременное использование двух связанных полей / внешних ключей
У меня есть три модели:
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
Я хочу, чтобы для каждого экземпляра игры был доступ к TeamBoxScore
, который соответствует как Game
экземпляру, так и Team
экземпляру, на который ссылается home_team
/away_team
.
Есть ли способ сделать так, чтобы game.home_team.team_boxscore
ссылался только на boxscore для этого game_id
? Или, наоборот, я могу указать, какой game.game_boxscore
я хочу вернуть (тот, который соответствует home_team
или тот, который соответствует away_team
)? Я хочу минимизировать количество запросов и избежать длительных итеративных решений.
Я пробовал аннотировать поля из каждого экземпляра TeamBoxScore
в каждый Game
(ссылаясь на связанное поле game_boxscore
), но это приводит к удвоению размера набора запросов и размещению значений из домашнего и гостевого бокскора в отдельных экземплярах. Я пробовал обращаться к связанному полю 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. Есть идеи? Мне кажется, что я упускаю что-то очень простое.
Пробовали ли вы вместо этого аннотировать идентификаторы TeamBoxScore?
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