I'm using Django Rest Framework in an API project and am trying to figure out if there's a way to use two different serializers with the generic views (e.g. CreateAPIView). I want to use one serializer for deserializing a POST request, and a different one for serializing the resulting response.
Here's what I'm trying to do; I'll illustrate using the Album/Track examples from the docs:
The model that I'm working with has a ForeignKey relationship. In the API, I'd like to just be able to include the FK in the request when assigning the relationship, so in the serializer I'm using a PrimaryKeyRelatedField, similar to how the AlbumSerializer handles the relationship to Tracks:
class CreateAlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
However, on the response, I'd like to include a full representation of the Album using a ModelSerializer, not just the PK, slug, etc., something like this:
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = ('order', 'title', 'duration')
The generic DRF views allow you to either specify the serializer_class
or override the get_serializer_class
method, but I can't figure out how to use that to accomplish what I'm after.
Is there something obvious that I'm missing? This seems like a reasonable thing to want to do, but I can't seem to grok how to get it done.
Overwrite the generic mixins in the DRF ViewSet
. For example:
class MyViewSet(CreateModelMixin, MultipleSerializersViewMixin, ViewSet):
serializer_class = CreateAlbumSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
saved = self.perform_create(serializer)
serializer = self.get_serializer(instance=saved, serializer_class=AlbumSerializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
return serializer.save()
MultipleSerializersViewMixin
is taken from django-rest-framework-braces
.
Customize to_representation
of the CreateAlbumSerializer
. For example:
class MyViewSet(CreateModelMixin, ViewSet):
serializer_class = CreateAlbumSerializer
class CreateAlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
def to_representation(self, instance):
data = super(CreateAlbumSerializer, self).to_representation(instance)
data['tracks'] = TrackSerializer(instance=instance.tracks).data
return data
I personally like approach #1 instead of #2 even though it is more verbose since it does not leak any of the custom create/response logic to serializers. I think serializer should just know how to serialize and all custom requirements to pick different serializers for the job should be done in the views.
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