Django:按查询按顺序分组?

2024-04-18 14:19:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我想根据一个点域给用户分配排名。在

使用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]不为null…”在Python中?在


Tags: the代码用户byiftopstatus情况
2条回答

我只需要使用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个循环:

^{pr2}$

最后一个改进,也许保留一个奖品列表而不是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?。在

快速未经测试的代码,希望您能理解:

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

欢迎更多优雅的解决方案!在

相关问题 更多 >