Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework: `get_serializer_class` called several times, with wrong value of request method

Using a ModelViewSet, is it normal for get_serializer_class to be called multiple times for a single request, when accessing the browsable API? And that the value of self.method.request changes between each call?

I've created a small test project to show the behaviour. In project/example/views.py there's a ThingViewSet with a custom get_serializer_class, which prints the current request method.

If you start the server and navigate to http://127.0.0.1:8000/things/1/, the output will be something like:

./manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).
May 19, 2015 - 08:51:34
Django version 1.8.1, using settings 'project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Method is: GET
Method is: PUT
Method is: PATCH
Method is: PUT
[19/May/2015 08:51:40]"GET /things/1/ HTTP/1.1" 200 11679

Clearly, get_serializer_class is called 4 times, with different values (GET, PUT, PATCH, PUT), although only a single GET request is performed.

Strange thing is, that this doesn't happen if you request it as JSON:

./manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).
May 19, 2015 - 10:25:57
Django version 1.8.1, using settings 'project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Method is: GET
[19/May/2015 10:26:22]"GET /things/?format=json HTTP/1.1" 200 49

And the problem is that the request method in the last call to get_serializer_class of the browsable API is PUT (which is obviously wrong for a GET request) and then we end up using the wrong serializer for the request, given that different serializers are returned for different request methods, which we do in our real life project (e.g. for read and write operations).

Can anybody shed some light on what is going on? Why is get_serializer_class called several times for the browsable API, with wrong method values?

like image 558
decibyte Avatar asked May 19 '15 10:05

decibyte


People also ask

What is renderers in Django REST framework?

The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types.

What is Restapi in Django?

REST APIs are an industry-standard way for web services to send and receive data. They use HTTP request methods to facilitate the request-response cycle and typically transfer data using JSON, and more rarely - HTML, XML and other formats.

Why use Django REST framework instead of Django?

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django.


1 Answers

The reason why you are seeing get_serializer_class being called multiple times is because you are using the browsable API. If you test it out without using the browsable API, for example by forcing the JSON renderer (?format=json or an Accept header), you will only see it called one.

The browsable API generates the forms that are displayed based on the serializer, so get_serializer_class is called once for each form and possible request type.

So while the first request, a GET makes sense for the original serializer that is used to handle the response data (the specific object, in this case), the next three are custom to the browsable API. These are the calls that happen, in the following order, to get_serializer which you are seeing

  1. The raw PUT form (for entering any request body).
  2. The raw PATCH form.
  3. The full PUT form (contains the instance data by default).

The method is being changed with the override_method with function which emulates the request method being overridden, which would normally happen in a POST request that needed a different method.

like image 145
Kevin Brown-Silva Avatar answered Sep 18 '22 20:09

Kevin Brown-Silva