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.
Your code is using serializer only for validation, but it is possible use it to insert or update new objects on database calling serializer. save() . To save foreign keys using django-rest-framework you must put a related field on serializer to deal with it. Use PrimaryKeyRelatedField .
The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.
From DRF v3 onwards, setting a field as read-only or write-only can use serializer field core arguments mentioned as follows. write_only. Set this to True to ensure that the field may be used when updating or creating an instance, but is not included when serializing the representation.
mixins are classes that generally inherit from object (unless you are django core developer) mixins are narrow in scope as in they have single responsibility. They do one thing and do it really well. mixins provide plug-in functionality.
Assuming that you used StringRelatedField
in your MovieSerializer
like this:
class MovieSerializer(serializers.ModelSerializer):
genre = serializers.StringRelatedField(many=True)
class Meta:
model = Movie
fields = [
'popularity',
'director',
'genre',
'imdb_score',
'name',
]
the result would look like this when retrieving a list of movies:
[
{
"popularity": 83.0,
"director": "Victor Fleming",
"genre": [
"Adventure",
"Family",
"Fantasy",
"Musical"
],
"imdb_score": 8.3,
"name": "The Wizard of Oz"
}
]
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
:
from rest_framework import serializers
from .models import Genre, Movie
class GenreRelatedField(serializers.RelatedField):
def display_value(self, instance):
return instance
def to_representation(self, value):
return str(value)
def to_internal_value(self, data):
return Genre.objects.get(name=data)
class MovieSerializer(serializers.ModelSerializer):
genre = GenreRelatedField(
queryset=Genre.objects.all(),
many=True
)
class Meta:
model = Movie
fields = (
'popularity',
'director',
'genre',
'imdb_score',
'name',
)
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 method to_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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With