Django如何从关联Reviews模型获取Gig平均评分及解决KeyError问题
报错原因
你触发KeyError的核心问题是:调用aggregate(Avg('rating'))时没有给聚合结果指定别名,Django默认生成的聚合结果键名为rating__avg,和你代码中取值的rating_avg不匹配,访问不存在的键就触发了报错。
另外你原代码中直接判断self._average_rating,如果Gig实例没有被提前annotate该属性(比如单独查询单个Gig的场景),会直接触发AttributeError。
修复步骤
1. 修复模型属性代码
修改Gigs模型的average_rating属性,处理别名和空属性、空评论的情况:
@property def average_rating(self): # 先判断是否存在annotate的平均字段,避免属性不存在报错 if hasattr(self, '_average_rating') and self._average_rating is not None: return self._average_rating # 给聚合结果指定别名,和取值键匹配 avg = self.reviews.aggregate(rating_avg=Avg('rating'))['rating_avg'] # 没有评论时返回默认值0,避免序列化报错 return avg if avg is not None else 0
2. 优化序列化器(可选)
因为average_rating已经是模型的只读属性,可以直接用ReadOnlyField简化代码:
class GigsSerializer (serializers.ModelSerializer): average_rating = serializers.ReadOnlyField() class Meta: model = Gigs fields = ['id','title','category','price','details','seller','images', 'average_rating']
新增评论自动更新平均评分的实现方案
你现在的实现是每次查询时实时计算平均值,数据量较大时性能较低,推荐用Django信号实现评论变动时自动更新Gig的平均评分,直接存储字段减少查询开销:
步骤1:给Gigs模型加存储平均评分的字段
class Gigs(models.Model): # 原有字段保持不变 average_rating = models.DecimalField(max_digits=3, decimal_places=1, default=0)
修改完成后执行迁移命令:python manage.py makemigrationspython manage.py migrate
步骤2:编写信号触发更新逻辑
在你的app下新建signals.py,写入如下代码:
from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.db.models import Avg from .models import Reviews, Gigs @receiver([post_save, post_delete], sender=Reviews) def update_gig_rating(sender, instance, **kwargs): target_gig = instance.item avg_result = target_gig.reviews.aggregate(avg=Avg('rating'))['avg'] target_gig.average_rating = avg_result if avg_result is not None else 0 target_gig.save(update_fields=['average_rating'])
步骤3:注册信号
修改你app的apps.py,在AppConfig类中添加ready方法注册信号:
from django.apps import AppConfig class 你的App名称Config(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = '你的App名称' def ready(self): # 导入信号模块完成注册 import 你的App名称.signals
步骤4:简化视图代码
改用存储字段后不需要每次查询都做聚合,视图的查询集可以直接简化:
def get_queryset(self): return Gigs.objects.all()
内容的提问来源于stack exchange,提问作者Ruma
相关产品推荐
相关产品推荐

