You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 makemigrations
python 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 13:15:03