Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using extra() on ValuesQuerySet in Django

I'm trying to calculate a percentage with two values which are themselves aggregated. The SQL query that explains what I'm after is as follows:

SELECT (SUM(field_a) / SUM(field_b) * 100) AS percent
FROM myapp_mymodel 
GROUP BY id
ORDER BY id

I tried to use the following to construct a QuerySet, but unfortunately it doesn't contain the extra field:

MyModel.objects.values('id').annotate(
   sum_field_a=Sum('field_a'),
   sum_field_b=Sum('field_b')).extra(
      select={'percent': 'sum_field_a / sum_field_b * 100'})

What irritates me is that - according to the Django documentation - this seems to be the way to go:

When a values() clause is used to constrain the columns that are returned in the result set […] instead of returning an annotated result for each result in the original QuerySet, the original results are grouped according to the unique combinations of the fields specified in the values() clause. An annotation is then provided for each unique group; the annotation is computed over all members of the group.

Source: http://docs.djangoproject.com/en/dev/topics/db/aggregation/#values

If you use a values() clause after an extra() clause, any fields defined by a select argument in the extra() must be explicitly included in the values() clause. However, if the extra() clause is used after the values(), the fields added by the select will be included automatically.

Source: http://docs.djangoproject.com/en/dev/ref/models/querysets/#values

like image 790
jnns Avatar asked Mar 04 '11 11:03

jnns


People also ask

How do I use extra in Django?

If you use a values() clause after an extra() clause, any fields defined by a select argument in the extra() must be explicitly included in the values() clause. However, if the extra() clause is used after the values(), the fields added by the select will be included automatically.

Can we use multiple filters in Django?

The filter() method takes the arguments as **kwargs (keyword arguments), so you can filter on more than one field by sepearting them by a comma.


1 Answers

Aggregate expressions allow such expressions on Aggregate functions easily since Django 1.8 without the problematic 'extra()' method.

qs = (
    MyModel.objects.values('id')
    .annotate(percent=Sum('field__a') / Sum('field__b') * 100)
    .order_by('id')
)
>>> print(str(qs.query))
SELECT id, ((SUM(field_a) / SUM(field_b)) * 100) AS percent
FROM app_mymodel GROUP BY id ORDER BY id ASC

(The mentioned issue #15546 has been closed soon by a documentation note that extra() after values() will not work - commit a4a250a.)

like image 180
hynekcer Avatar answered Sep 21 '22 21:09

hynekcer