django: группировка в запросе order_by? - PullRequest
0 голосов
/ 29 марта 2010

Я хочу присвоить рейтинг пользователям, основываясь на поле очков.

Достаточно легко подумать с помощью запроса 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 ]

1 голос
/ 29 марта 2010

Я бы просто использовал 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)

Предложением этого подхода является то, что вы не выполняете группировку в базе данных, а вместо этого делаете это в памяти. Это может быть проблемой, если есть много пользователей. См. Как сделать запрос как GROUP BY в django? , чтобы узнать, как это сделать в базе данных.

0 голосов
/ 29 марта 2010

Быстрый и непроверенный код, надеюсь, вы поняли:

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

Более элегантные решения приветствуются!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...