Decide serializer class dynamically based on viewset actions in Django Rest Framework (DRF)

Practically and mostly, I have experienced that your application wants to expose only required information based on what client asks (requests) for the resource hence your application needs different serializer classes based on it when you’re writing APIs in DRF.

For example, to get the list of users in pagination style, you’re only interested to show their name, email, company they’re working at, but for a specific user’s detailed view you might want to give other info too such as address, phone, job title, department, record creation/modification time and record created/modified by whom, etc…

Let’s start with Company and User models defined in the gist as follows:

User model has been extended from Common & AbstractUser models and has a field company refers to Company which means mostly each user (except is_superuser, is_staff members) should belong to a company with at least SYS_USER role.

Now, let’s create a few serializer classes for these models as follows:

Here, we created two serializer classes for each model, one will be used for listing purpose and another one will be used for detailed lookup.

Now, your application wants to use UserSerializer when the client hits the /users/ API endpoint and UserDetailSerializer for the/users/{pk}/ API endpoint. Under a viewset you can do this by overriding get_serializer_class method. You can introduce a new variable serializer_action_classes along with serializer_class under a viewset which maps the viewset action name to serializer class. Now, your overridden get_serializer_class will look like,

overriding get_serializer_class method | the image created using tool

If an API endpoint matches to viewset action (e.g, /users/ API endpoint to list viewset action) then the serializer class will be used if it’s defined in serializer_action_classes variable else it will fall back to use serializer_class, the default one. You can create mixin for this so that you can use the functionality in other viewsets too without overriding get_serializer_class method under each viewset. So, the mixin GetSerializerClassMixin would look like as follows:

Now, you just have to inherit GetSerializerClassMixin class in your viewset classes and just mention the serializer_action_class variable so that our final viewset code would like:

Okay! But I want to limit the user’s information based on the current session user role.

Let’s say Frank and Joe belong to RainDrops named Company with roles SYS_ADMIN and SYS_USER respectively so that Frank can also manage all users under RainDrops company. So, Frank is able to see full details (UserDetailSerializer data) of all users from RainDrops company where Joe is able to see only limited info (UserSerializer data). Note that, Frank and Joe both are able to see only limited info of a user who is not part of RainDropscompany!

Okay, this is a very special case which I came across in one of my projects. For this, I had to write custom logic by overriding get_serializer_class method, had to dig into DRF source code by taking help from some of code from drf-extensions as well.

If you’ve noticed in the above gist, we’re no longer using GetSerializerClassMixin class for the inheritance plus I changed the value of serializer_class to UserSerializer with mentioning of serializer_detail_class = UserDetailSerializer additional variable. In the above code, we’re getting detailed value from the URL (e.g, /users/2/ => 2) using lookup from kwargs and then using that value we get the user instance. The logic will use serializer_detail_class in two cases to send the detailed info of a user else, it will use serializer_class for limited info of a user which are as follows:

  1. If current session user is looking for their own profile (when Frank or Joe is looking into his data).
  2. If SYS_ADMIN role user is looking for other users for the same company (whenFrank is looking at Joe's data).

Credits and References

The credit for using a mixin to choose serializer class per viewset action really goes to the two links (posted below), I just explained it here step by step :). As well as, I covered the special case about limiting on delivering data to clients (with Frank and Joe guys example). The focus of this article is to deal with what kind of information your APIs want to deliver to clients with simple as well as the special use cases based on viewset action. That’s why I covered both things in this article.

I hope this will help to deal with the problems similarly I had. The entire sample source code is available at this repository. Thank you very much for reading the article.