我想根据点数字段为用户分配排名。
您可以轻松地使用order_by
查询。但是,我如何处理两个用户具有相同点数并需要共享相同排名的情况?我应该使用annotate
查找具有相同点数的用户吗?
我现在的代码,以及我想做的伪代码描述如下。
top_users = User.objects.filter(problem_user=False).order_by('-points_total')
# Wrong - in pseudocode, this should be
# Get the highest points_total, find all the users with that points_total,
# if there is more than one user, set status to 'Joint first prize',
# otherwise set status to 'First prize'
top_users[0].status = "First prize"
if (top_users[1]):
top_users[1].status = "Second prize"
if (top_users[2]):
top_users[2].status = "Third prize"
if (top_users[3]):
top_users[3:].status = "Highly commended"
上述代码不涉及两个用户拥有相同点数并需要分享二等奖的情况。我想我需要创建一个查找points_total唯一值的查询,并进行某种嵌套排名?
它也无法应对有时少于4个用户的事实 - 如果top_users [1]在Python中不是空的话,有没有人知道我怎么做(伪代码)?
答案 0 :(得分:1)
我只使用itertools.groupby
。类似的东西:
top_users = [(k, list(g)) for k,g in groupby(top_users, key=lambda x: x.score))]
for u in top_users[0][1]:
u.status = 'First prize'
for u in top_users[1][1]:
u.status = 'Second prize'
for u in top_users[2][1]:
u.status = 'Third prize'
for score, users in top_users[3:]:
for u in users:
u.status = 'Highly recommended'
甚至更好,使用itertools.count
而不是4个循环:
top_users = [(k, list(g)) for k,g in groupby(top_users, key=lambda x: x.score))]
for c, (score, group) in zip(count(0), top_users):
if c == 0:
prize = 'First prize'
elif c == 1:
prize = 'Second prize'
elif c == 2:
prize = 'Third prize'
else:
prize = 'Highly recommended'
map(lambda x: setattr(x, 'status', prize), group)
最后一个改进,可能是保留奖品清单而不是if语句。
top_users = [(k, list(g)) for k,g in groupby(top_users, key=lambda x: x.score))]
prize_list = ['First prize', 'Second prize', 'Third prize', 'Highly recommended']
for c, (score, group) in zip(count(0), top_users):
prize = prize_list[c] if c < len(prize_list) else prize_list[-1]
map(lambda x: setattr(x, 'status', prize), group)
这种方法的警告是你没有在数据库中进行分组,而是在内存中进行分组。如果有很多用户,这可能是一个问题。有关如何在数据库中执行此操作的一些指导,请参阅How to query as GROUP BY in django?。
答案 1 :(得分:0)
快速&amp;未经测试的代码,希望你能得到这个想法:
top_users = User.objects.filter(...)
prizes = ['First prize', 'Second', 'Third', ...]
prize = 0
previous_points = None
try:
for user in top_users:
if user.points_total < previous_points:
# always skipped in first iteration
prize += 1
user.status = prizes[prize] # raise IndexError when out of prizes
previous_points = user.points_total
except IndexError:
pass
欢迎更优雅的解决方案!