32

How do I set a different Serializer for list and detail view while using viewsets.ModelViewSet and HyperlinkedSerializer?

I see how to do it with viewsets.ViewSet by defining list and retrive, (here's an example) but I don't know how to elegantly adapt it to viewsets.ModelViewSet

Timmy O'Mahony
  • 53,000
  • 18
  • 155
  • 177
andilabs
  • 22,159
  • 14
  • 114
  • 151
  • http://stackoverflow.com/questions/22616973/django-rest-framework-use-different-serializers-in-the-same-modelviewset – andilabs Jul 18 '14 at 10:45

5 Answers5

59

I've adapted an answer from "Django rest framework, use different serializers in the same ModelViewSet" that serves me very well, and I hope you'll find useful:

class MyModelViewSet(viewsets.MyModelViewSet):

    queryset = MyModel.objects.all()
    serializer_class = MyModelListSerializer
    detail_serializer_class = MyModelDetailSerializer

    def get_serializer_class(self):
        if self.action == 'retrieve':
            if hasattr(self, 'detail_serializer_class'):
                return self.detail_serializer_class

        return super(MyModelViewSet, self).get_serializer_class()

In this case, you're just specifying your two serializers and using the one depending on the action. However, this can be made more general (for all actions) as follows:

class MyModelViewSet(viewsets.MyModelViewSet):

    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    action_serializers = {
        'retrieve': MyModelDetailSerializer,
        'list': MyModelListSerializer,
        'create': MyModelCreateSerializer
    }

    def get_serializer_class(self):

        if hasattr(self, 'action_serializers'):
            return self.action_serializers.get(self.action, self.serializer_class)

        return super(MyModelViewSet, self).get_serializer_class()
Rashid Mahmood
  • 313
  • 4
  • 10
bbengfort
  • 5,254
  • 4
  • 44
  • 57
1

Viewsets extend the class GenericAPIView, so you can use this part of the documentation to solve your problem. Basically, what you need is to override get_serializer_class and to return a different serializer based on your request.

AdelaN
  • 3,366
  • 2
  • 25
  • 45
0

I've created this small package for this job. drf_custom_viewsets.

It has CustomSerializerViewSet, which inherits from ModelViewSet, which lets you set different serializers for different actions.

Mahammad Adil Azeem
  • 9,112
  • 13
  • 57
  • 84
0

As of 2021, I will do it differently, a better and more generic way around is doing something like this:

class PlayersListViewSet(viewsets.ModelViewSet):
    queryset = Player.objects.all()
    serializer_class = PlayersListSerializer
    http_method_names = ['get', 'post']
    pagination_class = None
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['name']

    serializer_class_by_action = {
        'retrieve': PlayersDetailSerializer,
        'list': PlayersListSerializer,
    }

    def get_serializer_class(self):

        if hasattr(self, 'serializer_class_by_action'):
            return self.serializer_class_by_action.get(self.action, self.serializer_class)

        return super(MyModelViewSet, self).get_serializer_class()

    def get_queryset(self):
        queryset = Player.objects.all()
        team_id = self.request.query_params.get('team', None)

        if team_id:
            try:
                queryset = queryset.filter(team=team_id)
            except ValueError:
                raise exceptions.ParseError()
        return queryset

Here action is the method used by the serializer, list in case of def list, retrieve in case of def retrieve and so on..

ofnowhere
  • 1,114
  • 2
  • 16
  • 40
0

Thanks to @bbengfort, I've provided a simple solution without the need to create a new ViewSet.

Different Serializer Per Action In the ViewSet in Django

TL;DR

In the following code, we are overriding get_serializer_class based on Django documentation and we're specifying different serializers per action if it's needed:

Views.py

class TestAPIView(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated,)
    serializer_class = TestAPISerializer

    serializer_class_by_action = {
        'update_me': UpdateMeSerializer,
    }

    def get_serializer_class(self):

        if hasattr(self, 'serializer_class_by_action'):
            return self.serializer_class_by_action.get(self.action, self.serializer_class)

        return self.serializer_class

    @action(detail=True, methods=['patch'], url_name='Update Me', url_path='updateme')
    def update_me(self, request, pk=None):
        # Write your own logic
        return Response("OK")

Serializers.py

class UpdateMeSerializer(serializers.Serializer):
    count = serializers.CharField(required=False, allow_null=True, default=10)
    class Meta:
        fields = ['count']

Soroosh Khodami
  • 1,185
  • 9
  • 17