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

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)

错误原因

  1. 字段放错位置:avg_rating和num_rating被定义在Review模型里,但代码中却在操作watchlist.avg_rating和watchlist.num_rating,而WatchList模型根本没有这两个属性,直接触发属性不存在的错误。
  2. 逻辑错误:新增评论时应该用当前评论的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:35:21