Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we use serializer_class attribute with APIView(django rest framework)?

As per the DRF documentation, the serializer_class attribute should be set when using GenericAPIView. But why does the serializer_class attribute even works with APIView?

Here is my API code:

class UserView(APIView):

    serializer_class = SignupSerializer

    @transaction.atomic
    def post(self, request):
        email = get_data_param(request, 'email', None)
        password = get_data_param(request, 'password', None)

        params = request.POST.copy()
        params["username"] = email

        serializer = UserSerializer(data=params)
        if serializer.is_valid(raise_exception=True):
            user = serializer.save()
            user.set_password(password)
            user.save()
            params["user"] = user.id

        serializer = CustomProfileSerializer(data=params)
        if serializer.is_valid(raise_exception=True):
            profile = serializer.save()
            return Response(response_json(True, profile.to_dict(), None))

class SignupSerializer(serializers.Serializer):
    email = serializers.EmailField(max_length=100)
    password = serializers.CharField(max_length=50)

When I browse this API in the browser it does show the email and password fields as input but if I don't set this serializer_class attribute, no input fields are shown.
Ideally, this serializer_class attribute should not work with APIView. I have searched a lot but there is nothing available related to this.

Can anyone please provide an explanation for this behavior? Thanks.

like image 418
hardcore developer Avatar asked Aug 27 '16 21:08

hardcore developer


People also ask

What is APIView in Django REST framework?

REST framework provides an APIView class, which subclasses Django's View class. APIView classes are different from regular View classes in the following ways: Requests passed to the handler methods will be REST framework's Request instances, not Django's HttpRequest instances.

What is difference between APIView and Viewset?

APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.

How do you pass extra context data to Serializers in Django REST framework?

In function-based views, we can pass extra context to serializer with “context” parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with “self. context”. From example, to get “exclude_email_list” we just used code 'exclude_email_list = self.

What is Serializer_class?

serializer_class - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the get_serializer_class() method.


2 Answers

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!

like image 64
John Moutafis Avatar answered Sep 19 '22 12:09

John Moutafis


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})
like image 23
Mohammadali Davarzani Avatar answered Sep 22 '22 12:09

Mohammadali Davarzani