Django Rest Framework Many To Many проблема с сериализатором

Я создаю API в Django Rest Framework и у меня возникла проблема с отношением ManyToMany.

Я пытаюсь создать фильм и присвоить ему жанр (отношения ManyToMany между фильмом и жанром).

Вот мои модели:

class Genre(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(null=False,blank=False,max_length=50)
    
    def __str__(self):
        return self.name

class Movie(models.Model):
    id = models.IntegerField(primary_key=True,null=False,blank=False)
    adult = models.BooleanField(null=False,blank=False)
    backdrop_path = models.CharField(null=True,blank=True,max_length=200)
    budget = models.BigIntegerField (default=0)
    homepage = models.CharField(null=True,blank=True,max_length=200)
    original_language = models.CharField(max_length=10)
    original_title = models.CharField(max_length=200)
    overview = models.CharField(max_length=1000)
    poster_path = models.CharField(null=True,blank=True,max_length=200)
    release_date = models.DateField(null=True,blank=True,)
    revenue = models.BigIntegerField (default=0)
    runtime = models.IntegerField(null=True,blank=True)
    status = models.CharField(max_length=50)
    tagline = models.CharField(null=True,blank=True,max_length=100)
    title = models.CharField(max_length=200)
    genres = models.ManyToManyField(Genre,blank=True)
    
    def __str__(self):
        return self.title

вот мои сериализаторы:

class GenresSerializers(serializers.ModelSerializer):
    class Meta:
        model = Genre
        fields = ['id','name']
        
    def create(self,validated_data):
        
        instance, _ = Genre.objects.get_or_create(**validated_data)
        return instance
    
class MovieGenreSerializer(serializers.ModelSerializer):
    class Meta:
        model = Genre
        fields = ['id']
        
    def create(self, validated_data):
        instance, _ = Genre.objects.get(**validated_data)
        return instance
    
class MoviesSerializer(serializers.ModelSerializer):
    genres = GenresSerializers(many= True)
    
    class Meta:
        model = Movie
        fields = [ 'id', 'adult', 'backdrop_path', 'budget', 'homepage', 'original_language', 'original_title', 'overview', 'poster_path', 'release_date', 'revenue', 'runtime', 'status', 'tagline', 'title', 'genres'
            ]
        # extra_kwargs = {'id': {'read_only': True}}
        
        
    def create(self, validated_data):
        genres_data = validated_data.pop('genres', []) 
        
        genresInstances = []
        for genre in genres_data:
            genresInstances.append(Genre.objects.get(pk = genre['id']))
        movie = Movie.objects.create(**validated_data)
        movie.genre.set(genresInstances)
        return movie

и мои наборы взглядов:

class MovieViewSet(viewsets.ModelViewSet):
    queryset = Movie.objects.all()
    serializer_class = MoviesSerializer

Когда я пытаюсь создать новый фильм с этим телом запроса (у меня уже есть созданные жанры):

{
  "id": 425,
  "adult": false,
  "backdrop_path": "/kK5OeulwVDniPgjNOGHvzcORzdG.jpg",
  "budget": 59000000,
  "homepage": "http://www.iceagemovies.com/films/ice-age",
  "original_language": "en",
  "original_title": "Ice Age",
  "overview": "With the impending ice age almost upon them, a mismatched trio of prehistoric critters – Manny the woolly mammoth, Diego the saber-toothed tiger and Sid the giant sloth – find an orphaned infant and decide to return it to its human parents. Along the way, the unlikely allies become friends but, when enemies attack, their quest takes on far nobler aims.",
  "poster_path": "/stQoHvo0q6ZcEkji2Z1wlAxKnDq.jpg",
  "release_date": "2002-03-10",
  "revenue": 383257136,
  "runtime": 81,
  "status": "Released",
  "tagline": "They came. They thawed. They conquered.",
  "title": "Ice Age",
  "genres": [
    {
      "id": 16
    },
    {
      "id": 35
    },
    {
      "id": 10751
    },
    {
      "id": 12
    }

  ]
}

он возвращается следующим образом:

{
  "genres": [
    {
      "id": [
        "genre with this id already exists."
      ]
    },
    {
      "id": [
        "genre with this id already exists."
      ]
    },
    {
      "id": [
        "genre with this id already exists."
      ]
    },
    {
      "id": [
        "genre with this id already exists."
      ]
    }
  ]
}

РЕДАКТИРОВАТЬ:

редактировать в админке сайта

я редактирую в админке сайта, добавляю, удаляю жанр к существующему фильму и сохраняю, но он не сохраняется и база данных не меняется

Не используйте субсериализатор, используйте, например, SlugRelatedFieldSerializer [drf-doc]:

class MoviesSerializer(serializers.ModelSerializer):
    genres = serializers.SlugRelatedField(
        many=True, queryset=Genre.objects.all(), slug_field='name'
    )

    class Meta:
        model = Movie
        fields = [
            'id',
            'adult',
            'backdrop_path',
            'budget',
            'homepage',
            'original_language',
            'original_title',
            'overview',
            'poster_path',
            'release_date',
            'revenue',
            'runtime',
            'status',
            'tagline',
            'title',
            'genres',
        ]
        # extra_kwargs = {'id': {'read_only': True}}

    # no create

Затем вы создаете элементы с помощью:

{
    # …,
    "genres": ["horror", "music", "musical"]
}

В MoviesSerializer есть проблемы с производительностью, поскольку вы вызываете Genre.objects.get(pk = genre['id']) в цикле for. Это приведет к многократному обращению к базе данных.

Поскольку вы уже создали жанр. Попробуйте этот сериализатор:

class MoviesSerializer(serializers.ModelSerializer):
    genres = serializers.ListField()
    
    class Meta:
        model = Movie
        fields = [ 'id', 'adult', 'backdrop_path', 'budget', 'homepage', 'original_language', 'original_title', 'overview', 'poster_path', 'release_date', 'revenue', 'runtime', 'status', 'tagline', 'title', 'genres'
            ]
        # extra_kwargs = {'id': {'read_only': True}}
        
        
    def create(self, validated_data):
        genres_data = validated_data.pop('genres', []) 
        
        genresIds = [g["id"] for g in genres_data]
        genresInstances = Genre.objects.filter(id__in=genresIds)

        movie = Movie.objects.create(**validated_data)
        movie.genres.set(genresInstances)
        return movie
Вернуться на верх