M
M
Michael2015-09-23 16:29:01
Django
Michael, 2015-09-23 16:29:01

How to group_by a specific field in Django 1.8?

There is an old code on Django 1.6
. It groups the results as follows:

>>> 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'

But in Django 1.8 this hack doesn't work.
>>> 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'

How can I group the results by the database field I need?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
J
jsse, 2015-09-23
@MikeVL

Now, if there is aggregation, all fields from select are added to group_by, although the code describes that this hack is compatible, but no.
You will have to override the standard grouping function:

_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

As an option, expand with a nested query, but this may affect performance.
something like:
users = Active.objects.values('user_id').distinct()
post_set = Active.objects.filter(rubrica__pk=1, type__pk=1, user__in=users)

there will be one query but nested select will appear

A
Alexander, 2015-09-26
@syschel

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() # Сбросил сортировку

I receive a request
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

If I don't make .order_by() empty. then pulls from the model settings or other sorts that are higher than the request
dates = models.DateField(_(u'Дата календаря'), blank=False)
...
class Meta:
    ordering = ['-dates', '-create']

And then groups on two fields.
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

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question