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

Django多对多序列化器Post API报错:AttributeError求助

多对多关联(通过中间表Cast)创建电影接口AttributeError问题排查与解决

问题场景

通过Cast中间表在Movie和Person模型间建立多对多关联,使用CastSerializer实现POST接口创建新电影及演员关联,但调用接口时返回AttributeError。

期望请求体格式

{
  ...
  "title": "string",
  "genres": [
    0
  ],
  "cast": [
    {
      "charactor": "string",
      "cast": 0,
      "order": 2147483647
    }
  ]
}

报错信息

AttributeError at /movie/
Got AttributeError when attempting to get a value for field `cast` on serializer `MoviesSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Movie` instance.
Original exception text was: 'Movie' object has no attribute 'person'.

模型代码

class Movie(models.Model):
    ...
    cast = models.ManyToManyField(Person,related_name='cast_movies', through="Cast")
    
    def __str__(self):
        return self.title
    
class Cast(models.Model):
    id = models.AutoField(primary_key=True,null=False,blank=False)
    movie = models.ForeignKey(Movie,on_delete=models.CASCADE,related_name='casts')
    cast = models.ForeignKey(Person,on_delete=models.CASCADE,related_name='person')
    charactor = models.CharField(max_length=200,null=True,blank=True,default='')
    order = models.IntegerField(null=True)
    
    def __str__(self):
        return self.cast.name + " ("+ self.movie.title + ")"

序列化器代码

class CastSerializer(serializers.ModelSerializer):
    class Meta:
        model = Cast
        fields = ['charactor','cast','order']    
    
class MoviesSerializer(serializers.ModelSerializer):
    genres = serializers.SlugRelatedField(
        many=True, queryset=Genre.objects.all(),slug_field='id'
    )
    cast = CastSerializer(many = True)
    class Meta:
        model = Movie
        fields = [ ...,'cast']
        
    def create(self, validated_data):
        genres_data = validated_data.pop('genres', []) 
        
        cast_data = validated_data.pop('cast', []) 
        movie = Movie.objects.create(**validated_data)
        for cast in cast_data:
            Cast.objects.create(movie_id = movie.id,**cast)
        genresInstances = []
        for genre in genres_data:
            genresInstances.append(genre)
        
        movie.genres.set(genresInstances)
        return movie

问题原因

  1. 序列化器字段映射冲突:MoviesSerializer中定义的cast = CastSerializer(many=True),与Movie模型的cast多对多字段(关联Person)重名。序列化器尝试读取Movie实例的cast字段时,会获取Person集合,但CastSerializer期望的是Cast中间表实例,导致逻辑混淆。
  2. 反向关联名称冲突:Cast模型中指向Person的字段cast的related_name设为person,名称过于通用,引发ORM解析关联时的混淆,直接导致报错中'Movie' object has no attribute 'person'的问题。

修复步骤

1. 修改模型反向关联名称

将Cast模型中指向Person字段的related_name改为更清晰的名称,避免冲突:

class Cast(models.Model):
    id = models.AutoField(primary_key=True,null=False,blank=False)
    movie = models.ForeignKey(Movie,on_delete=models.CASCADE,related_name='casts')
    # 重命名related_name,避免与其他逻辑冲突
    cast = models.ForeignKey(Person,on_delete=models.CASCADE,related_name='cast_roles')
    charactor = models.CharField(max_length=200,null=True,blank=True,default='')
    order = models.IntegerField(null=True)
    
    def __str__(self):
        return self.cast.name + " ("+ self.movie.title + ")"

2. 调整序列化器字段映射

在MoviesSerializer中,使用Movie模型通过Cast中间表的反向关联casts(对应Cast模型中movie字段的related_name),并通过source参数映射到请求体的cast字段:

class MoviesSerializer(serializers.ModelSerializer):
    genres = serializers.SlugRelatedField(
        many=True, queryset=Genre.objects.all(),slug_field='id'
    )
    # 使用source指定从反向关联casts读取数据,保持请求体字段名不变
    cast = CastSerializer(source='casts', many=True)
    class Meta:
        model = Movie
        fields = [ ...,'cast']
        
    def create(self, validated_data):
        genres_data = validated_data.pop('genres', []) 
        cast_data = validated_data.pop('cast', []) 
        movie = Movie.objects.create(**validated_data)
        # 直接关联movie实例,无需手动传movie_id
        for cast in cast_data:
            Cast.objects.create(movie=movie,**cast)
        # 简化genres关联逻辑
        movie.genres.set(genres_data)
        return movie

3. 可选:增强字段验证

为确保请求中cast对应的Person存在,可在CastSerializer中显式指定字段类型:

class CastSerializer(serializers.ModelSerializer):
    cast = serializers.PrimaryKeyRelatedField(queryset=Person.objects.all())
    class Meta:
        model = Cast
        fields = ['charactor','cast','order']

内容的提问来源于stack exchange,提问作者12341413451354134

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 18:53:26