How to subtract two annotated columns on Django QuerySets?
Question:
I need to be able to sort on the aggregate of two annotated columns
So I’d like to do something like this:
c = c.annotate(metric=Sum('results__metric'))
c = c.annotate(metric_prior=Sum('results__metric_prior'))
c = c.annotate(variance=F('metric')-F('metric_prior')) #doesn't work, for demonstrative purposes only
and then:
c = c.order_by('variance')
Does anyone know how to accomplish something like the above?
Answers:
Ticket is there for more than 4 years (by 2014), but it can be accomplished with a little .extra()
query, like this:
items = MyModel.objects.extra(
select = {'variance': 'SUM(relatedModel__someField) - SUM(relatedModel__someField)'},
)
Yes, it can be a bit unpredictable with different DBMS. But if you limit the syntax inside extra to very common SQL it should work more or less everywhere.
Actually,
c = c.annotate(variance=F('metric')-F('metric_prior'))
works as you would like it to starting with Django 1.8.
Moreover, you can also order by an expression, which means you can just use:
c = c.order_by(F('metric') - F('metric_prior'))
or even just:
c = c.order_by(Sum('results__metric') - Sum('results__metric_prior'))
I need to be able to sort on the aggregate of two annotated columns
So I’d like to do something like this:
c = c.annotate(metric=Sum('results__metric'))
c = c.annotate(metric_prior=Sum('results__metric_prior'))
c = c.annotate(variance=F('metric')-F('metric_prior')) #doesn't work, for demonstrative purposes only
and then:
c = c.order_by('variance')
Does anyone know how to accomplish something like the above?
Ticket is there for more than 4 years (by 2014), but it can be accomplished with a little .extra()
query, like this:
items = MyModel.objects.extra(
select = {'variance': 'SUM(relatedModel__someField) - SUM(relatedModel__someField)'},
)
Yes, it can be a bit unpredictable with different DBMS. But if you limit the syntax inside extra to very common SQL it should work more or less everywhere.
Actually,
c = c.annotate(variance=F('metric')-F('metric_prior'))
works as you would like it to starting with Django 1.8.
Moreover, you can also order by an expression, which means you can just use:
c = c.order_by(F('metric') - F('metric_prior'))
or even just:
c = c.order_by(Sum('results__metric') - Sum('results__metric_prior'))