serializers.py
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
views.py
class MovieList(generics.ListCreateAPIView):
queryset = Movie.objects.all().order_by('-id')[:10]
serializer_class = MovieSerializer
permission_classes = (IsAuthenticated,)
def list(self, request):
queryset = self.get_queryset()
serializer = MovieSerializer(queryset, many=True)
return Response(serializer.data)
def post(self, request, format=None):
data = request.data
if isinstance(data, list):
serializer = self.get_serializer(data=request.data, many=True)
else:
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I send my data like this and it works correctly.
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
1,
2,
3,
4
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
But I want to send the data somewhat like this:
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
"Adventure",
"Family",
"Fantasy",
"Musical"
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
See the Genre List - Instead of Primary Key I am sending name
.
These are my Models:
class Genre(models.Model):
name = models.CharField(max_length=30, unique=True) # make unique
def __str__(self):
return self.name
class Movie(models.Model):
popularity = models.FloatField(max_length=10)
director = models.CharField(max_length=30)
genre = models.ManyToManyField(Genre)
imdb_score = models.FloatField(max_length=10)
name = models.CharField(max_length=30)
Even when I am listing data or getting the data it should send genre name instead of id. I tried to use StringRelatedField
, but it gave me error.
NotImplementedError at /api/movie/ StringRelatedField.to_internal_value() must be implemented.
Assuming that you used
StringRelatedField
in yourMovieSerializer
like this:the result would look like this when retrieving a list of movies:
But if you want to create a new movie, then it won't work because
StringRelatedField
is read-only.You can however create your custom related field.
This is the complete
serializers.py
:This is a simple example that can be highly customized in many ways.
The method
display_value
defines how the object Genre is displayed, for example in the form. Here it just returns the object Genre i.e. the output of__str__
.The method
to_representation
defines how the object Genre is displayed in the output (JSON or XML). It's very similar to the previous method, but here we explicitly have to convert Genre to string. Certainly you can create a more complex output according to your requirements.The method
to_internal_value
solves your actual problem by getting an object Genre for the given value. If you have a more complex methodto_representation
here you would need expanded logics to get the appropriate object.Using this approach you can post a JSON in your desired form, specifying the genre names instead of their ids.
I hope this example helps other people too.
The easy solution would be to change the
Genre
model to usename
as primary key, like so:Not that it matters much here, but this will also save a column in the database, since the auto generated
id
column will disappear :)Update
After some discussion in the comments to this answer, I find it important to mentions that using ANY type as primary key, you should also avoid changing that field afterwards.
This is because a change to the primary key, also necessitates an update to all the foreign keys pointing to that primary key, and (to put it in terms of this question) even though your table with genres may be relatively small, you may have a significant amount of movies pointing to each genre.
On solution is to override
genre
field in your serializer, to accept a list of strings like this:And on validate method try to fetch each object by their names and put in a new list and update
data
result with new list of objects. (I know it's not the cleanest solutions, but it works fine)you could also try to use
MethodSerializer
or define aGenreSerializer
and fetch objects in that by their names and use that in the parent serializer as an input.Override the
create()
method of the serializer as below,If you always pass name to your serializer, you can add foreign-key field in Model defination. link For you case