Как я могу заставить GROUP BY работать с ORM Django, где мне нужны все поля и объект

Мне нужно сгруппировать все записи по пользователю и получить счет, выполнив что-то вроде этого:

class Promotion(models.Model):
    pass

class Entry(models.Model):
    user = models.ForeignKey('User');
    promotion = models.ForeignKey('Promotion')


def get_uniques(promotion_id):
    promotion = Promotion.objects.get(promotion_id)
    entries = promotion.entry_set.annotate(Count('user'))
    return entries

Однако он возвращает одного и того же пользователя несколько раз. Я также пробовал следующее после просмотра StackOverflow, и, похоже, он делает что-то другое, чем то, что я хочу:

promotion.entry_set.annotate(Count('user')).order_by('user')[:10]
promotion.entry_set.all().values('user').annotate(entry_count=Count('user')).order_by()
Entry.objects.filter(promotion=promotion).annotate(Count('user')).order_by('user')

В основном я пытаюсь сделать это, предоставляя мне объект Entry для каждого пользователя:

Entry.objects.raw("""
    SELECT *
    FROM promotion_entry
    WHERE promotion_id = %s
    GROUP BY user_id""", (promotion_id,))

Затем я выполню второй запрос, чтобы получить количество входов, но все еще не идеальное. Могу ли я сделать GROUP BY без необработанных данных?

Кажется, есть билет, который позволит мне делать то, что я хочу в будущем, на багтрекере, включив DISTINCT ON: https://code.djangoproject.com/ticket/6422


person Kit Sunde    schedule 07.12.2011    source источник


Ответы (1)


Если вы хотите подсчитать записи для каждого пользователя, используйте:

promotion.entry_set.all().values('user').annotate(entry_count=Count('id')).order_by()

Если вам нужна запись для каждого пользователя, используйте:

promotion.entry_set.all().values('user').annotate(entry_id=Max('id')).order_by()

Это даст вам id записей, используйте __in для получения самих объектов.

person DrTyrsa    schedule 07.12.2011
comment
Это не совсем то, что я хочу. Это эквивалент SELECT user_id, COUNT(*) FROM giveaways_entry WHERE promotion_id = 57 GROUP BY user_id; - person Kit Sunde; 07.12.2011
comment
@KitSunde А что ты хочешь получить? Все записи для каждого пользователя или количество записей для каждого пользователя? Если второе - см. мой ответ, если первое - это не делается с GROUP BY (order_by + itertools.regroup сделают свое дело). - person DrTyrsa; 08.12.2011
comment
Я выразился немного двусмысленно, согласен. Я хочу иметь один объект Entry для каждого пользователя, но не более одного. Я обработаю подсчет отдельно, поскольку он не является частью модели, и, похоже, Джанго просто так его не добавит. Разве itertools.regroup не будет перебирать каждую совпадающую строку? Это то, чего я пытался избежать, ранее код, который я выполнял all() и повторял набор запросов, отфильтровывая уже увиденных пользователей, но поскольку я хочу выполнить разбиение на страницы, я бы предпочел иметь правильный фрагмент результата, поскольку набор запросов может стать довольно большим. - person Kit Sunde; 08.12.2011
comment
@KitSunde Я хочу иметь один объект Entry для каждого пользователя - какой объект? Любой или удовлетворяющий некоторым критериям (например, последний)? - person DrTyrsa; 08.12.2011
comment
Просто любой, не имеет значения, последняя ли это запись или первая для этого пользователя, в моем случае использования. - person Kit Sunde; 08.12.2011