Decide serializer class dynamically based on viewset actions in Django Rest Framework (DRF)
Practically and for most cases that I have observed, it so happens that an application tends to expose only required information based on what a client asks (requests) the resource. Hence the application needs different serializer classes based on the requirement at the time of writing APIs in DRF.
For instance, to get the list of users in pagination style, you may simply be interested in showing their names, email IDs and the organization they’re working for. But for a specific user’s detailed view you might want to give other information as well such as addresses, phone numbers, 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 for listing purpose and another one 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,
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:
At this point, you only have to inherit GetSerializerClassMixin class in your viewset classes and mention the serializer_action_class variable so that our final viewset code would look as follows:
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 a company called RainDrops with roles SYS_ADMIN
and SYS_USER
respectively. Note that this allows Frank to manage all users under RainDrops company. Therefore, Frank is able to see full details (UserDetailSerializer
data) of all users from RainDrops company whereas Joe is able to see only limited info (UserSerializer
data). Also, Frank and Joe both are able to see only limited information of a user that is not part of RainDrops company.
Okay, this is a very special case which I came across during one of my projects. For the assignment, 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 through drf-extensions as well.
In case you 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 serializer_detail_class = UserDetailSerializer
as an 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 this value we get the user instance. The logic will use serializer_detail_class
in two cases: to send the detailed info of a user or use serializer_class
for limited info of a user which are as follows:
- If current session user is looking for their own profile (when Frank and Joe is looking into his data).
- If a user (with the
SYS_ADMIN
role) is looking at other users’s profile for the same company (when Frank is looking at Joe’s profile data).
Credits and References
The credit for using a mixin to choose serializer class per viewset action goes to the two links (posted below), my role was limited to simplifying and explaining it step by step :). Additionally, I covered the special case about limiting on delivering data to clients (with Frank and Joe 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 special use-cases based on viewset action.