Как сделать group_by по определённому полю в Django 1.8?

Есть старый код на Django 1.6
В нём делается группировка результатов следующим образом:
>>> post_set = Active.objects.filter(rubrica__pk=1, type__pk=1).order_by('user', '-pub_date')
>>> post_set.query.group_by = ['user_id']
>>> str(post_set.query)
>>> 'SELECT `doska_active`.`id`, `doska_active`.`post_id` FROM `doska_active` WHERE (`doska_active`.`type_id` = 1  AND `doska_active`.`rubrica_id` = 1 ) GROUP BY (user_id) ORDER BY `doska_active`.`user_id` ASC, `doska_active`.`pub_date` DESC'

Но в Django 1.8 данный хак не работает.
>>> post_set = Active.objects.filter(rubrica__pk=1, type__pk=1).order_by('user', '-pub_date')
>>> post_set.query.group_by = ['user_id']
>>> str(post_set.query)
>>> 'SELECT `doska_active`.`id`, `doska_active`.`post_id` FROM `doska_active` WHERE (`doska_active`.`type_id` = 1 AND `doska_active`.`rubrica_id` = 1) GROUP BY `doska_active`.`id` ORDER BY `doska_active`.`user_id` ASC, `doska_active`.`pub_date` DESC'


Как можно сгруппировать результаты по нужному мне полю БД ?
  • Вопрос задан
  • 3076 просмотров
Решения вопроса 1
@jsse
Сейчас если есть агрегация добавляются в group_by все поля из select, хотя в коде описано что этот хак совместим, но неет.

Прийдется перекрывать стандартную функцию группировки:

_get_group_by = SQLCompiler.get_group_by
def custom_group_by(self, select, order_by):
    if self.query.group_by is True or not self.query.group_by:
        return _get_group_by(self, select, order_by)
    expressions = []
    if self.query.group_by is not True:
        for expr in self.query.group_by:
            if not hasattr(expr, 'as_sql'):
                expressions.append(self.query.resolve_ref(expr))
            else:
                expressions.append(expr)
    if len(expressions):
        having = self.query.having.get_group_by_cols()
        for expr in having:
            expressions.append(expr)
        result = []
        seen = set()
        expressions = self.collapse_group_by(expressions, having)
        for expr in expressions:
            sql, params = self.compile(expr)
            if (sql, tuple(params)) not in seen:
                result.append((sql, params))
                seen.add((sql, tuple(params)))
        if result:
            return seen
SQLCompiler.get_group_by = custom_group_by

# Потом использовать как обычно:
post_set = Active.objects.filter(rubrica__pk=1, type__pk=1).order_by('user', '-pub_date')
post_set.query.group_by = ['user_id']
print post_set.query


Как вариант расширить вложенным запросом, но это может сказаться на производительности.
что-то подобное:
users = Active.objects.values('user_id').distinct()
post_set = Active.objects.filter(rubrica__pk=1, type__pk=1, user__in=users)


будет один запрос, но появится вложенный select
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
syschel
@syschel
freelance/python/django/backend
Django 1.8.4
queryset.filter(**params).only('dates', 'type_date').select_related(*select_rel).order_by('-dates', '-create')
queryset.values('dates').annotate(count=Count('dates')).order_by() # Сбросил сортировку


Получаю запрос
SELECT `dates`, COUNT(`dates`) AS `count` 
FROM `gigdate_dateall` 
WHERE (`dates` IN (2015-08-31, 2015-09-01, 2015-09-02, 2015-09-03) AND `type_date` = 0) 
GROUP BY `dates` ORDER BY NULL


Если .order_by() не делаю пустым. то тянет из настроек модели или других вышестоящих над запросом сортировок
dates = models.DateField(_(u'Дата календаря'), blank=False)
...
class Meta:
    ordering = ['-dates', '-create']

И тогда группирует по двум полям.
SELECT `dates`, COUNT(`dates`) AS `count` 
FROM `gigdate_dateall` 
WHERE (`dates` IN (2015-08-31, 2015-09-01, 2015-09-02, 2015-09-03) AND `type_date` = 0) 
GROUP BY `dates`, `create` ORDER BY `dates` DESC, `create` DESC
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы