[Fixed]-Can we use serializer_class attribute with APIView(django rest framework)?

3👍

I think this can help you.
create serializer.py and write:

from rest_framework import serializers

from .models import User


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('email', 'password')

and views.py:

from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import UserSerializer
from .models import User


class AddUserAPIView(APIView):
    def post(self, request):
        user_serializer = UserSerializer(data=request.data)
        if user_serializer.is_valid():
            user = user_serializer.save() 
            user.set_password(user_serializer.data["password"])
            return Response({'message': 'user added successfully!'})

        return Response({'message': user_serializer.errors})

2👍

You are absolutely right!!:
APIView doesn’t utilize a serializer_class (by default) because it is not meant to handle any request processing logic!

What happens though is that the BrowsableAPIRenderer that is used to render the API in the browser checks for a serializer_class attribute and set’s it as the View serializer if it exists. We can see this in the BrowsableAPIRenderer code:

  1. The _get_serializer class of the renderer:

    def _get_serializer(self, serializer_class, view_instance, request, *args, **kwargs):
        kwargs['context'] = {
           'request': request,
           'format': self.format,
           'view': view_instance
       }
       return serializer_class(*args, **kwargs)
    
  2. And the way it is used to set the renderer serializer if it exists, inside the get_rendered_html_form:

    • Line 483: has_serializer_class = getattr(view, 'serializer_class', None)

    • Lines 497 to 509:

      if has_serializer:
          if method in ('PUT', 'PATCH'):
              serializer = view.get_serializer(instance=instance, **kwargs)
          else:
              serializer = view.get_serializer(**kwargs)
      else:
          # at this point we must have a serializer_class
          if method in ('PUT', 'PATCH'):
              serializer = self._get_serializer(view.serializer_class, view,
                                                request, instance=instance, **kwargs)
          else:
              serializer = self._get_serializer(view.serializer_class, view,
                                                request, **kwargs)
      

In essence, you accidentally override the BrowserAPIRenderer‘s default behavior regarding the APIView by providing the serializer_class attribute. For what is worth, my opinion on the matter is that this should not be possible!

2👍

I use the django rest framework default get_schema_view() to provide auto-generated openapi schema from which I auto generate a javascript client for.

This works for ViewSets, but the payload wasn’t being provided for views defined by APIView.

Where I have defined serializers, I found that adding get_serializer() method to my APIView classes allowed the schema to be generated with the serializer defined payload.

from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import UserSerializer
from .models import User


class AddUserAPIView(APIView):
    def get_serializer(self, *args, **kwargs):
        return UserSerializer(*args, **kwargs)

    def post(self, request):
        user_serializer = UserSerializer(data=request.data)
        if user_serializer.is_valid():
            user = user_serializer.save() 
            user.set_password(user_serializer.data["password"])
            return Response({'message': 'user added successfully!'})

        return Response({'message': user_serializer.errors})
👤monkut

Leave a comment