如何使用djangos ORM构建单个查询,以便为每位玩家获得低于1.000的最高分?我使用的是django 1.7 rc2和python 3.4。
models.py:
from django.db import models
class Player(models.Model):
pass
class Score(models.Model):
player = models.ForeignKey(Player)
score = models.PositiveIntegerField(default=0)
到目前为止我的解决方案:
tests.py
from django.test import TestCase
from bar.models import Player, Score
class BarTest(TestCase):
def test_do_it(self):
p1 = Player.objects.create()
p2 = Player.objects.create()
Score.objects.create(player=p1, score=100)
Score.objects.create(player=p1, score=12000)
Score.objects.create(player=p2, score=10000)
Score.objects.create(player=p2, score=500)
Score.objects.create(player=p2, score=900)
players = Player.objects.all()
for player in players:
score = Score.objects.filter(player=player, score__lte=1000).order_by('score').last()
if score:
print(player.id, score.id, score.score)
我不想迭代python中的每个玩家并对每个玩家进行查询,而只用一个查询来解决这个问题(不用编写原始sql)。
答案 0 :(得分:2)
使用select_related()
,您可以一次性获取所有分数及其玩家:
from collections import defaultdict
scores = defaultdict(list)
# Fetch all scores (with score <= 1000), including their players.
for score in Score.objects.filter(score__lte=1000).select_related('player'):
# Group scores by their player.
scores[score.player.id].append(score)
# Get max score for each player.
for p in scores:
max_score = max(scores[p], key=lambda s: s.score)
print(p, max_score.id, max_score.score)
答案 1 :(得分:0)
怎么样:
all_scores = Score.objects.filter(score__lte=1000).order_by('player', '-score')
current_player = None
scores = []
for score in all_scores:
if current_player != score.player:
current_player = score.player
scores.append(score)
只有一次迭代超过分数。排序由数据库完成。
修改强>
最后,我找到了一个解决方案:
Player.objects.filter(score__score__lte=1000
).annotate(max_score=Max('score__score'))
构建此SQL查询:
SELECT
"bar_player"."id",
MAX("bar_score"."score") AS "max_score"
FROM
"bar_player"
LEFT OUTER JOIN
"bar_score"
ON
( "bar_player"."id" = "bar_score"."player_id" )
WHERE
"bar_score"."score" <= 1000
GROUP BY
"bar_player"."id"