Django аннотирует и фильтрует
Это продолжение предыдущего вопроса, но я включу все подробности здесь.
Я создаю игру, одним из элементов которой является то, что игроки голосуют друг за друга.
Вот модели, которые я установил (только соответствующие поля)
#models.py
class Game(models.Model):
gamecode = ShortUUIDField(length=4, max_length=4, unique=True)
phasenumber = models.IntegerField(default=1)
isActive = models.BooleanField(default=True)
class Player(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
isPlaying = models.BooleanField(default=True)
class PlayerVote(models.Model):
byplayer = models.ForeignKey(Player, on_delete=models.CASCADE)
forplayer = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="voteforplayer")
gamephasenumber = models.IntegerField()
timestamp = models.DateTimeField(auto_now_add=True)
Когда пользователь присоединяется к игре, он получает запись в модели "Player". Когда они отдают голос за другого игрока, в модель "PlayerVote" добавляется запись, показывающая, какой игрок проголосовал (byplayer), за кого он проголосовал (forplayer), и фазу, в которой находится игра (gamephasenumber) (это просто целое число, которое увеличивается с каждой фазой игры)
Я хотел бы создать QuerySet, содержащий каждого игрока и количество голосов, полученных им в этой фазе игры.
Я могу сделать следующее, чтобы получить голоса, которые они получили за все этапы
playerswithvotes = Player.objects.select_related('game').filter(game = activegame.game, game__phasenumber = activegame.game.phasenumber, isPlaying = True).annotate(votesreceived=Count('voteforplayer')).order_by('-votesreceived')
Но как я могу показать только голоса, которые они получили в этом номере фазы?
Я пробовал каждый из следующих вариантов:
votesthisround = Count("playervote", filter=Q(playervote__gamephasenumber = activegame.game.phasenumber))
playerswithvotes = Player.objects.select_related('game').filter(game = activegame.game, game__phasenumber = activegame.game.phasenumber, isPlaying = True).annotate(votesreceived=votesthisround).order_by('-votesreceived')
playerswithvotes = Player.objects.select_related('game').filter(game = activegame.game, game__phasenumber = activegame.game.phasenumber, isPlaying = True).annotate(votesreceived=Count('voteforplayer', filter=Q(playervote__gamephasenumber = activegame.game.phasenumber))).order_by('-votesreceived')
playerswithvotes = Player.objects.select_related('game').filter(game = activegame.game, game__phasenumber = activegame.game.phasenumber, isPlaying = True).annotate(votesreceived=Count('voteforplayer')).filter(playervote__gamephasenumber = activegame.game.phasenumber).order_by('-votesreceived')
Попробуйте выполнить этот запрос:
PlayerVote.objects.filter(forplayer__game=activegame.game, gamephasenumber=activegame.game.phasenumber).values("forplayer").annotate(votes=Count("pk"))
Использование .values()
позволяет нам использовать GROUP BY
в SQL-запросе, который генерирует Django, так что мы можем получить результат, сгруппированный по получателю PlayerVotes. Результат выглядит следующим образом: <QuerySet [{'forplayer': 1, 'votes': 1}, {'forplayer': 3, 'votes': 2}]>
Если вам нужна сводка о том, какие игроки получили голоса в каждом раунде, вы можете удалить фазу игры из фильтра и добавить ее в .values()
часть запроса:
PlayerVote.objects.filter(forplayer__game=activegame.game).values("forplayer", "gamephasenumber").annotate(votes=Count("pk"))
Вывод выглядит следующим образом: <QuerySet [{'forplayer': 1, 'gamephasenumber': 1, 'votes': 1}, {'forplayer': 2, 'gamephasenumber': 2, 'votes': 2}, {'forplayer': 3, 'gamephasenumber': 1, 'votes': 2}, {'forplayer': 3, 'gamephasenumber': 2, 'votes': 1}]>