如何在Django中实现按游戏阶段统计玩家得票数的QuerySet
问题描述
我正在开发一款包含玩家互投机制的游戏,已在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')
解决方案
核心问题是反向关联名称使用错误:Player与PlayerVote的反向关联通过related_name="voteforplayer"定义,所以过滤投票阶段时要使用这个名称,而非playervote。
正确实现代码如下:
current_phase = activegame.game.phasenumber playerswithvotes = Player.objects.select_related('game') .filter(game=activegame.game, isPlaying=True) .annotate( votesreceived=Count( 'voteforplayer', filter=Q(voteforplayer__gamephasenumber=current_phase) ) ) .order_by('-votesreceived')
细节说明:
- 关联名称修正:用
voteforplayer__gamephasenumber替代错误的playervote__gamephasenumber,匹配模型中定义的related_name。 - 代码简化:
game__phasenumber过滤可以省略,因为game=activegame.game已经锁定目标游戏,其phasenumber就是当前阶段。 - 保留所有活跃玩家:不要在
annotate后添加阶段过滤,否则会丢失当前阶段未获票的玩家,我们需要保留所有活跃玩家的记录(得票数为0的也会显示)。 - 可读性优化:提取
current_phase变量,避免重复调用activegame.game.phasenumber,让代码更清晰。
内容的提问来源于stack exchange,提问作者phuefi
相关产品推荐
相关产品推荐

