Django更新多对一字段时遇AttributeError:WatchList无avg_rating属性
问题:AttributeError: Object has no Attribute "avg_rating"
该错误出现在views.py的perform_create函数中,更新多对一(ManyToOne)字段时触发。需求是收到评论时,更新与WatchList通过外键关联的电影的平均评分。
现有代码
models.py
class WatchList(models.Model): title = models.CharField(max_length=50) storyline = models.CharField(max_length=200) platform = models.ForeignKey(StreamPlatform, on_delete=models.CASCADE, related_name="watchlist") active = models.BooleanField(default=True) created = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title class Review(models.Model): username = models.ForeignKey(User, on_delete=models.CASCADE) rating = models.PositiveIntegerField(validators = [MinValueValidator(1), MaxValueValidator(5)]) comment = models.CharField(max_length=200, null=True) watchlist = models.ForeignKey(WatchList, on_delete=models.CASCADE, related_name="reviews") avg_rating = models.FloatField(default=0) num_rating = models.IntegerField(default=0) active = models.BooleanField(default=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self): return self.comment
serializer.py
class ReviewSerializer(serializers.ModelSerializer): username = serializers.StringRelatedField(read_only=True) class Meta: model = Review exclude = ["watchlist"] class WatchListSerializer(serializers.ModelSerializer): ### 用关联序列化器列出一部电影的所有评论 reviews = ReviewSerializer(many=True, read_only=True) class Meta: model = WatchList fields = "__all__"
views.py
class ReviewCreateView(generics.CreateAPIView): serializer_class = ReviewSerializer permission_classes = [IsAuthenticated] def get_queryset(self): return Review.objects.all() def perform_create(self, serializer): ## 通过电影主键提交评论 primary_key = self.kwargs.get("pk") watchlist = WatchList.objects.get(pk=primary_key) # 禁止同一用户对同一电影重复评论 username = self.request.user review_queryset = Review.objects.filter(watchlist=watchlist, username=username) if review_queryset.exists(): raise ValidationError("You cannot comment on the same movie Twice") ## 待修改的代码段 if watchlist.avg_rating == 0: watchlist.avg_rating = serializer.validated_data['avg_rating'] else: watchlist.avg_rating = (watchlist.avg_rating + serializer.validated_data['avg_rating'])/2 watchlist.num_rating += 1 watchlist.avg_rating.save() serializer.save(watchlist=watchlist, username=username)
错误原因
- 字段放错位置:
avg_rating和num_rating被定义在Review模型里,但代码中却在操作watchlist.avg_rating和watchlist.num_rating,而WatchList模型根本没有这两个属性,直接触发属性不存在的错误。 - 逻辑错误:新增评论时应该用当前评论的
rating计算电影的平均评分,而不是去取serializer.validated_data['avg_rating'](这个字段本身也不该属于评论)。
修复方案
步骤1:调整模型结构
把avg_rating和num_rating移到WatchList模型中,因为这两个字段是属于电影的统计数据:
# 修改后的models.py class WatchList(models.Model): title = models.CharField(max_length=50) storyline = models.CharField(max_length=200) platform = models.ForeignKey(StreamPlatform, on_delete=models.CASCADE, related_name="watchlist") active = models.BooleanField(default=True) created = models.DateTimeField(auto_now_add=True) # 新增电影的评分统计字段 avg_rating = models.FloatField(default=0) num_rating = models.IntegerField(default=0) def __str__(self): return self.title class Review(models.Model): username = models.ForeignKey(User, on_delete=models.CASCADE) rating = models.PositiveIntegerField(validators = [MinValueValidator(1), MaxValueValidator(5)]) comment = models.CharField(max_length=200, null=True) watchlist = models.ForeignKey(WatchList, on_delete=models.CASCADE, related_name="reviews") active = models.BooleanField(default=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self): return self.comment
步骤2:修正perform_create逻辑
用当前评论的rating计算新的平均评分,并且保存watchlist对象(不是单独保存字段):
# 修改后的views.py def perform_create(self, serializer): primary_key = self.kwargs.get("pk") watchlist = WatchList.objects.get(pk=primary_key) username = self.request.user review_queryset = Review.objects.filter(watchlist=watchlist, username=username) if review_queryset.exists(): raise ValidationError("你不能对同一电影重复评论") # 正确计算平均评分:总评分=当前平均*已有评论数 + 新评分 new_rating = serializer.validated_data['rating'] total_rating = watchlist.avg_rating * watchlist.num_rating + new_rating watchlist.num_rating += 1 watchlist.avg_rating = total_rating / watchlist.num_rating # 保存整个watchlist对象 watchlist.save() serializer.save(watchlist=watchlist, username=username)
可选优化:实时计算评分
如果不想手动维护avg_rating和num_rating,可以用Django的聚合查询实时计算,避免数据不一致:
from django.db.models import Avg, Count # 获取电影时实时计算评分 watchlist = WatchList.objects.annotate( avg_rating=Avg('reviews__rating'), num_rating=Count('reviews') ).get(pk=primary_key)
这种方式不需要在模型中存储统计字段,每次查询时实时计算,数据更准确。
内容的提问来源于stack exchange,提问作者Orynnn
相关产品推荐
相关产品推荐

