How to get average from set of objects in Django?
Question:
I have a simple rating system for a property. You give it a mark out of 5 (stars). The models are defined like this
def Property(models.Model)
# stuff here
def Rating(models.Model)
property = models.ForeignKey(Property)
stars = models.IntegerField()
What I want to do is get a property, find all the Rating objects, collect them, then get the average ‘stars’ from them.
Any ideas how to do this?
Answers:
You should use Aggregation(doc):
from django.db.models import Avg
p = Property.objects.get(...)
stars_average = p.rating_set.aggregate(Avg('stars')).values()[0]
A little bit unsure about my example though.
You can use aggregate() and annotate() with Avg() to average the stars in Rating
model and property by property as shown below. *I need to use order_by('pk')
with annotate()
otherwise values are printed in descending order:
from django.db.models import Avg
# Average the stars in "Rating" model
print(Rating.objects.aggregate(Avg('stars')))
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(
Rating.objects.filter(property=obj)
.aggregate(Avg('stars'))
)
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(obj.rating_set.aggregate(Avg('stars')))
print()
# Average the stars in "Rating" model property by property
qs = Property.objects.annotate(Avg('rating__stars')).order_by('pk')
for obj in qs:
print(obj.rating__stars__avg)
Then, these below are outputted on console:
{'stars__avg': 3.7}
{'stars__avg': 3.2}
{'stars__avg': 3.6}
{'stars__avg': 4.0}
{'stars__avg': 3.2}
{'stars__avg': 3.6}
{'stars__avg': 4.0}
3.2
3.6
4.0
And, you can change the default key stars__avg
for stars column to starsAvg
as shown below:
from django.db.models import Avg
# Average the stars in "Rating" model
print(Rating.objects.aggregate(starsAvg=Avg('stars')))
# ↑ Here
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(
Rating.objects.filter(property=obj)
.aggregate(starsAvg=Avg('stars'))
) # ↑ Here
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(obj.rating_set.aggregate(starsAvg=Avg('stars')))
# ↑ Here
print()
# Average the stars in "Rating" model property by property
qs = Property.objects.annotate(starsAvg=Avg('rating__stars')).order_by('pk')
for obj in qs: # ↑ Here
print(obj.starsAvg)
# ↑ Here
Then, the default key is changed as shown below:
{'starsAvg': 3.7}
{'starsAvg': 3.2}
{'starsAvg': 3.6}
{'starsAvg': 4.0}
{'starsAvg': 3.2}
{'starsAvg': 3.6}
{'starsAvg': 4.0}
3.2
3.6
4.0
I have a simple rating system for a property. You give it a mark out of 5 (stars). The models are defined like this
def Property(models.Model)
# stuff here
def Rating(models.Model)
property = models.ForeignKey(Property)
stars = models.IntegerField()
What I want to do is get a property, find all the Rating objects, collect them, then get the average ‘stars’ from them.
Any ideas how to do this?
You should use Aggregation(doc):
from django.db.models import Avg
p = Property.objects.get(...)
stars_average = p.rating_set.aggregate(Avg('stars')).values()[0]
A little bit unsure about my example though.
You can use aggregate() and annotate() with Avg() to average the stars in Rating
model and property by property as shown below. *I need to use order_by('pk')
with annotate()
otherwise values are printed in descending order:
from django.db.models import Avg
# Average the stars in "Rating" model
print(Rating.objects.aggregate(Avg('stars')))
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(
Rating.objects.filter(property=obj)
.aggregate(Avg('stars'))
)
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(obj.rating_set.aggregate(Avg('stars')))
print()
# Average the stars in "Rating" model property by property
qs = Property.objects.annotate(Avg('rating__stars')).order_by('pk')
for obj in qs:
print(obj.rating__stars__avg)
Then, these below are outputted on console:
{'stars__avg': 3.7}
{'stars__avg': 3.2}
{'stars__avg': 3.6}
{'stars__avg': 4.0}
{'stars__avg': 3.2}
{'stars__avg': 3.6}
{'stars__avg': 4.0}
3.2
3.6
4.0
And, you can change the default key stars__avg
for stars column to starsAvg
as shown below:
from django.db.models import Avg
# Average the stars in "Rating" model
print(Rating.objects.aggregate(starsAvg=Avg('stars')))
# ↑ Here
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(
Rating.objects.filter(property=obj)
.aggregate(starsAvg=Avg('stars'))
) # ↑ Here
print()
# Average the stars in "Rating" model property by property
for obj in Property.objects.all():
print(obj.rating_set.aggregate(starsAvg=Avg('stars')))
# ↑ Here
print()
# Average the stars in "Rating" model property by property
qs = Property.objects.annotate(starsAvg=Avg('rating__stars')).order_by('pk')
for obj in qs: # ↑ Here
print(obj.starsAvg)
# ↑ Here
Then, the default key is changed as shown below:
{'starsAvg': 3.7}
{'starsAvg': 3.2}
{'starsAvg': 3.6}
{'starsAvg': 4.0}
{'starsAvg': 3.2}
{'starsAvg': 3.6}
{'starsAvg': 4.0}
3.2
3.6
4.0