django:在order_by查询中分组?

时间:2010-03-29 13:14:48

标签: python django

我想根据点数字段为用户分配排名。

您可以轻松地使用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中不是空的话,有没有人知道我怎么做(伪代码)?

2 个答案:

答案 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

欢迎更优雅的解决方案!