DRF SerializerMethodField中如何使用_set计算平均评分
错误原因
aggregate()是Django QuerySet(查询集)对象的内置方法,只能作用于多条记录的集合上,不能直接在单个模型实例上调用。
你当前代码中get_average_rating方法接收的instance是单个FeedbackModel实例,直接调用instance.aggregate()必然抛出AttributeError,这是报错的核心原因。
你提到的_set是一对多关系下,从「一」侧模型访问关联「多」侧模型集合的反向关联语法:你的FeedbackModel通过外键business_account关联BusinessAccountModel,且没有自定义related_name,因此从商家实例访问关联所有评价的反向属性为feedbackmodel_set,这个属性返回的就是对应商家下所有评价的QuerySet,支持aggregate()聚合操作。
修复方案
根据你要实现的业务场景,二选一即可:
场景1:返回商家维度的平均评分(绝大多数业务的常规需求)
平均评分是商家维度的统计值,不属于单条评价,你之前把字段写在FeedbackSerializer里是放错了位置,应该迁移到BusiAccSerializer中:
from django.db.models import Avg class BusiAccSerializer(serializers.ModelSerializer): average_rating = serializers.SerializerMethodField() def get_average_rating(self, instance): # 这里的instance是单个商家实例,通过反向_set拿到所有关联评价的查询集再聚合 rating_avg = instance.feedbackmodel_set.aggregate(avg=Avg('rating'))['avg'] # 无评价时默认返回0,可根据业务需求调整 return round(rating_avg, 1) if rating_avg is not None else 0.0 class Meta: model = BusinessAccountModel fields = ['business_title', 'business_description', 'status', 'note', 'user', 'average_rating']
你要找的
_set就加在商家实例后面,也就是上面代码里的instance.feedbackmodel_set位置。
场景2:单条评价返回时附带所属商家的平均评分
如果确实需要在评价列表里带出每条评价对应商家的平均评分,只需要修改FeedbackSerializer的聚合逻辑,先取到关联商家再走反向关联查询即可:
from django.db.models import Avg class FeedbackSerializer(serializers.ModelSerializer): feedback_by_user_profile_pic = serializers.ImageField(source='feedback_by.profile_pic') average_rating = serializers.SerializerMethodField() def get_average_rating(self,instance): # 先取当前评价关联的商家,再通过反向_set拿到该商家所有评价聚合 merchant_feedback_qs = instance.business_account.feedbackmodel_set.all() rating_avg = merchant_feedback_qs.aggregate(avg=Avg('rating'))['avg'] return round(rating_avg, 1) if rating_avg is not None else 0.0 class Meta: model = FeedbackModel fields = ['feedback_text','rating','date','feedback_by_user_profile_pic','average_rating']
性能优化建议
如果是列表接口返回数据,上述写法会产生N+1查询问题(每返回一条记录就执行一次聚合查询),建议在视图层提前用annotate把平均评分注到查询结果上,大幅减少数据库查询次数:
以商家列表接口为例,视图层查询集写法:
from django.db.models import Avg queryset = BusinessAccountModel.objects.annotate( average_rating=Avg('feedbackmodel__rating') )
这种写法下序列化器不需要再写SerializerMethodField,直接声明average_rating = serializers.FloatField()即可取到预计算的评分值,性能提升明显。
内容的提问来源于stack exchange,提问作者Amol_G
相关产品推荐
相关产品推荐

