如何在Django中实现Annotate和Filter
这是对之前一个问题的进一步说明,不过我会在这里把所有细节都说清楚。
我正在制作一个游戏,其中一个元素是玩家之间互相投票。
以下是我设置的模型(只包含相关字段)
我的 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 个回答
试试这个查询:
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}]>