Reputation: 1639
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.
Upvotes: 8
Views: 3548
Reputation: 27861
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.
Upvotes: 8