While working with Django REST Framework aka DRF, we often wonder how to customize our response based on request parameters. May be we want to check something against the logged in user (request.user) ? Or may be we want to modify part of our response based on a certain request parameter? How do we do that? We will discuss a few use cases below.

ModelViewSet - Filtering based on request

This is very often required while using ModelViewSets. We have many Items in our database. But when listing them, we only want to display the items belonging to the current logged in user.

from rest_framework.permissions import IsAuthenticated

class ItemViewSet(ModelViewSet):
    permission_classes = (IsAuthenticated,)
    serializer_class = ItemSerializer

    def get_queryset(self):
        queryset = Item.objects.all().filter(user=request.user)

        another_param = self.request.GET.get('another_param')
        if another_param:
            queryset = queryset.filter(another_field=another_param)

        return queryset

If you are using the awesome ModelViewSet, you can override the get_queryset method. Inside it, you can access the request object as self.request. In the above example, we are only listing the items which has our current user set as their user field. At the same time, we are also filtering the queryset based on another parameter. Basically you have the queryset and self.request available to you, feel free to use your imagination to craft all the queries you need!

Serializers - Modifying Response based on request

What if we don’t want to display item_count for the users by default? What if we only want to display that field when a request parameter, show_count is set? We can override the serializer to do that.

class UserSerializer(ModelSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        context = kwargs.get('context', None)
        if context:
            request = kwargs['context']['request']

            show_count = request.GET.get('show_count')
            if show_count:
                self.fields['item_count'] = IntegerField(source="item_count")

When Serializers are constructed by DRF, it gets the request in the context. So we should always check if it exists and use it as needed. We can override the serializer fields by accessing self.fields.

Please note: The request object will be passed only if DRF constructs the serializer for you, for example when you just pass the serializer_class to a ModelViewSet. But if you are using the Serializer in your custom views, please do remember to pass the request manually, otherwise it won’t work.

item_serializer = ItemSerializer(item, context={"request": request})

In our case we have just used IntegerField. You can of course use another serializer to embed the full data of a related field.

Using request in Serializer Fields

Serializer fields have context too!

class ShortURLField(ReadOnlyField):
    def to_representation(self, value):
        return self.context['request'].build_absolute_uri(value)

and here’s the serializer:

class URLSerializer(ModelSerializer):
    short_url = ShortURLField()

    class Meta:
        model = URL
        fields = "__all__"

In the URL model, there is a method named short_url that returns a slug for that url. In our custom ShortURLField, we have customized the to_representation method to use the build_absolute_uri(value) method on current request for creating the full url from the slug.