8

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.

1 Answer 1

8

Approach #1

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.

Approach #2

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

Comparison

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.