'Get average of averages in django
I have three models like these
class School(models.Model):
# some fields
class Class(models.Model):
school = models.ForeignKey(School, related_name='classes')
class Student(models.Model):
class = models.ForeignKey(Class, related_name='students')
degree = models.IntegerField()
I want to order schools using average degree , but the average degree Isn't just the average of all the students as the number of the students in the same class are not equal
so I need to get some thing like this
from django.db.models import Prefetch, Avg
classes = Prefetch('classes', Class.objects.all().annotate(avg_degree=Avg('students__degree'))
# this line would through an error
qs = School.objects.all().prefetch_related(classes)
.annotate(avg_degree=Avg('classes__avg_degree')).order_by('avg_degree')
of course I can't just use
qs = School.objects.all().annotate(avg_degree=Avg('classes__student__degree'))
this would give wrong answers
Solution 1:[1]
Unfortunately, you cannot annotate on columns that are created using another annotate. Prefetch does not provide access to annotate'd fields, it's primarily used to filter. If you don't need to operate on that queryset further (need only values), you can use raw query solution:
from django.db.models import Avg, F
query = (
Class.objects
.annotate(avg_degree=Avg('students__degree'), s_id=F('school'))
.values('s_id', 'deg')
.query
)
sql, params = query.sql_with_params()
template = '''
SELECT
temp.s_id as id,
AVG(temp.deg) as degree
FROM ({}) "temp"
GROUP BY temp.s_id
'''
qs = School.objects.raw(template.format(sql), params)
# Now we have required field:
for obj in qs:
print(obj.id, obj.degree)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | SUTerliakov |
