I'm creating an API in Django Rest Framework and I have a problem with a ManyToMany relation.
I'm trying to create an movie and assign genre to it (ManyToMany relationship between movie and genre).
Here are my models:
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
here are my serializers:
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
and my viewsets:
class MovieViewSet(viewsets.ModelViewSet):
queryset = Movie.objects.all()
serializer_class = MoviesSerializer
When I try to create a new movie with this request body (I already have created genres):
{
"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
}
]
}
it return like this:
{
"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."
]
}
]
}
EDIT:
i have edit in admin site, add, delete genre to exist movie and save but it not save and the database not change
Don't use a subserializer, use for example a 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
You create the items then with:
{
# …,
"genres": ["horror", "music", "musical"]
}