Dynamically include/exclude fields to Django Rest Framwork serializers based on user requests

(Comments)

When using serializers with django rest framework, retrieving all fields of the models might cause unnecessary traffic. This can be avoided with careful design.

A large serializer

Let us say we have a nested serializer called 'UserDataSerializer` with 5 fields each of which is a serializer in turn.

class UserDataSerializer(serializers.ModelSerializer):
    profile = ProfileSerializer(read_only=True)
    address = AddressSerializer(read_only=True)
    card = CardDetailsSerializer(read_only=True)
    portfolio = PortfolioSerializer(many=True, read_only=True)
    interests = InterestSerializer(many=True, read_only=True)

It is quite possible that each of the field itself holds a lot of data and could be unnecessary in some scenarios. Either way, if you do not want to serve all the fields and want to serve them based on the request here is the solution.


Let us say the requested fields are passed as part of the parameter tab. For example, if the client only needs profile and address fields, the request URL would look like http://domain.com/path/to/data/?tab=profile&tab=address. We can now dynamically include/exclude the fields based on the URL parameters. Add all the fields to the serializer Meta attribute fields.

class Meta:
     model = UserDataModel
     fields = ('profile', 'address', 'card', 'portfolio', 'interests')

With these values, the serializer initially includes all the fields (by class definition). But we can override the fields in the serializer's __init__ method. Here is how we build our custom __init__ method.


First we call the __init__ method defined in the super class.

def __init__(self, *args, **kwargs):
    # First call the __init__ method of super class
    super(WebsiteSerializer, self).__init__(*args, **kwargs)

Then we get the list of tabs requested from URL parameters.

 if 'context' in kwargs:
    if 'request' in kwargs['context']:
        # Get the list of parameters named 'tab' as a python list
        tabs = kwargs['context']['request'].query_params.getlist('tab', [])

We can now set this list as the fields of the serializer with

self.fields = tabs

But it is possible that the list may contain undefined fields which cannot be served by the serializer and can result in exception. So we take care of this using sets. First remove duplicates in this list by constructing a set.

included = set(tabs)

Then we make a set of already existing fields and get a set of excluded fields.

existing = set(self.fields.keys())
excluded = existing - included

This set gives us the set of fields that needs to be excluded. Now we just pop the elements in the set from the fields of the serializer with

for other in excluded:
    self.fields.pop(other)

Our complete __init__ method will look like this.

def __init__(self, *args, **kwargs):
    super(WebsiteSerializer, self).__init__(*args, **kwargs)

    if 'context' in kwargs:
        if 'request' in kwargs['context']:
            tabs = kwargs['context']['request'].query_params.getlist('tab', [])
            if tabs:
                # tabs = tabs.split(',')
                included = set(tabs)
                existing = set(self.fields.keys())

                for other in existing - included:
                    self.fields.pop(other)

This serializer will include only those fields that are requested by the client. Hope this helps you as it did us.

Comments

Recent Posts

Archive

2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom