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.
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.
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.
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.
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.
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:
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)
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!
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})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With