2010-03-29 3 views
0

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

Прост достаточно продумать с запросом 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?

ответ

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) 

И последнее уточнение, может сохранить список приз вместо если заявления.

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? для получения некоторых рекомендаций относительно того, как это сделать в базе данных.

+0

отличный, подробный ответ - так что я принимаю его, хотя я на самом деле использовал быстрый и грязный код ниже (только потому, что он появился первым!). Спасибо. – AP257

0

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

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 

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

+0

Спасибо за это! Я должен был установить previous_points = top_users [0] .points_total в строке 4, чтобы заставить его работать, а затем user.save() внутри цикла for - но он выполнил эту работу. – AP257

+0

К сожалению, удалено несколько точек с запятой из кода, было слишком много кодирования JS :) Я действительно не понимаю, почему это не сработает с 'previous_points = None', потому что' i jholster

Смежные вопросы