Consider this case where I have a Book
and Author
model.
serializers.py
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = ('id', 'name')
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
class Meta:
model = models.Book
fields = ('id', 'title', 'author')
viewsets.py
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
This works great if I send a GET
request for a book. I get an output with a nested serializer containing the book details and the nested author details, which is what I want.
However, when I want to create/update a book, I have to send a POST
/PUT
/PATCH
with the nested details of the author instead of just their id. I want to be able to create/update a book object by specifying a author id and not the entire author object.
So, something where my serializer looks like this for a GET
request
class BookSerializer(serializers.ModelSerializer):
author = AuthorSerializer(read_only=True)
class Meta:
model = models.Book
fields = ('id', 'title', 'author')
and my serializer looks like this for a POST
, PUT
, PATCH
request
class BookSerializer(serializers.ModelSerializer):
author = PrimaryKeyRelatedField(queryset=Author.objects.all())
class Meta:
model = models.Book
fields = ('id', 'title', 'author')
I also do not want to create two entirely separate serializers for each type of request. I'd like to just modify the author
field in the BookSerializer
.
Lastly, is there a better way of doing this entire thing?
There is a feature of DRF where you can dynamically change the fields on the serializer http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields
My use case: use slug field on GET so we can see nice rep of a relation, but on POST/PUT switch back to the classic primary key update. Adjust your serializer to something like this:
class FooSerializer(serializers.ModelSerializer):
bar = serializers.SlugRelatedField(slug_field='baz', queryset=models.Bar.objects.all())
class Meta:
model = models.Foo
fields = '__all__'
def __init__(self, *args, **kwargs):
super(FooSerializer, self).__init__(*args, **kwargs)
try:
if self.context['request'].method in ['POST', 'PUT']:
self.fields['bar'] = serializers.PrimaryKeyRelatedField(queryset=models.Bar.objects.all())
except KeyError:
pass
The KeyError is sometimes thrown on code initialisation without a request, possibly unit tests.
Enjoy and use responsibly.
IMHO, multiple serializers are only going to create more and more confusion.
Rather I would prefer below solution:
I think this is the cleanest approach.
See my similar problem and solution at DRF: Allow all fields in GET request but restrict POST to just one field
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