如何在Django中从一组对象中获取平均值?

17 投票
2 回答
19892 浏览
提问于 2025-04-15 21:54

我有一个简单的评分系统,用来给房产打分。你可以给它打0到5颗星的分数。模型是这样定义的:

def Property(models.Model)
    # stuff here

def Rating(models.Model)
    property = models.ForeignKey(Property)
    stars = models.IntegerField()

我想做的是获取一个房产,找到所有的评分对象,把它们收集起来,然后计算出它们的平均星级。

有没有什么好的方法可以做到这一点?

2 个回答

0

你可以使用 aggregate()annotate() 方法,结合 Avg() 来计算 Rating 模型中星级的平均值,具体的做法如下。*我需要在使用 annotate() 时加上 order_by('pk'),否则输出的值会是降序排列:

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)

然后,控制台会输出以下内容:

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

另外,你可以把默认的键 stars__avg 改成 starsAvg,这样可以更清楚地表示星级列的平均值,具体做法如下:

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

这样,默认的键就会变成如下所示:

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

你应该使用聚合(文档)

from django.db.models import Avg

p = Property.objects.get(...)
stars_average = p.rating_set.aggregate(Avg('stars')).values()[0]

不过我对我的例子有点不太确定。

撰写回答