如何在Django中实现Annotate和Filter

0 投票
1 回答
49 浏览
提问于 2025-04-13 20:24

这是对之前一个问题的进一步说明,不过我会在这里把所有细节都说清楚。

我正在制作一个游戏,其中一个元素是玩家之间互相投票。

以下是我设置的模型(只包含相关字段)

我的 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)

当一个用户加入游戏时,他们会在“玩家”模型中获得一个条目。当他们为另一个玩家投票时,会在“玩家投票”模型中添加一个条目,记录是哪个玩家投票(byplayer),他们投给了谁(forplayer),以及游戏当前处于哪个阶段(gamephasenumber)(这个只是一个整数,每个阶段都会增加)。

我想做的是创建一个查询集,包含每个玩家在这个阶段获得的投票数量。

我可以通过下面的方式获取他们在所有阶段收到的投票

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')

1 个回答

1

试试这个查询:

PlayerVote.objects.filter(forplayer__game=activegame.game, gamephasenumber=activegame.game.phasenumber).values("forplayer").annotate(votes=Count("pk"))

使用 .values() 可以让我们在Django生成的SQL查询中使用 GROUP BY,这样我们就可以按玩家投票的接收者来分组输出结果。结果看起来像这样:<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}]>

撰写回答