Django中按关联外键聚合玩家总得分与游戏场次的实现
Django高效统计玩家得分总和与游戏场次
嘿,我来给你个能解决性能问题的方案——别再逐个玩家去查了,用Django ORM的聚合查询一次搞定,效率拉满!
先明确你的需求和现有模型
你需要获取每个玩家的总得分和游戏场次,现有模型大概是这样(我猜Game里的user外键应该是关联Player?如果是User的话替换一下就行):
class Player(models.Model): name = models.CharField() class Game(models.Model): points = models.IntegerField() user = models.ForeignKey(Player, on_delete=models.CASCADE) # 假设关联的是Player模型
期望输出是类似这样的结构:
{ "name": "asdf", "total_points": "9999", "games_played": "12" }
高效实现代码
直接用annotate()做聚合查询,所有统计在数据库层面完成,只发一次SQL请求,彻底告别N+1查询:
from django.db.models import Sum, Count # 执行聚合查询,给每个Player添加统计字段 player_stats = Player.objects.annotate( # 通过反向关联计算总得分,默认反向关联名是game_set,也可以直接写game__points total_points=Sum('game__points'), # 统计该玩家的游戏场次 games_played=Count('game') ).values('name', 'total_points', 'games_played') # 转换成你需要的输出格式(处理无游戏记录的玩家,把None转为0并转成字符串) final_result = [] for stat in player_stats: final_result.append({ "name": stat['name'], "total_points": str(stat['total_points'] or 0), "games_played": str(stat['games_played']) })
几个关键细节
- 反向关联命名:如果你的
Game模型外键加了related_name,比如user = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='games'),那代码里就可以写成Sum('games__points'),可读性更好 - 空值处理:如果某个玩家还没有玩过游戏,
Sum()会返回None,所以用or 0把它转为0,再转成字符串符合你的输出要求 - 性能优势:这种方式只需要一次数据库查询,比你原来逐个玩家查询的方式快得多,尤其是玩家数量多的时候差异会非常明显
内容的提问来源于stack exchange,提问作者kachus22
相关产品推荐
相关产品推荐

