Home

Sept. 26, 2017, 3 min read

DRF - Dynamically Specifying Fields for a Serializer

The Django REST Framework is very open for customization on all levels. Views accept the regular GET and POST arguments which we can use to derive a specific behaviour in how to process the request and produce a response. However we may also want to specify different serializations based on what the user requested, but it is not as apparent how to achieve that.

We can write multiple serializers for our use cases, but this quickly gets very redundant. In general, serializers have a context attribute that we can add items to to pass information to the serializer that it in turn may use to produce a different output. Some parts of the framework already do this automatically (ModelViewSets for example pass the original request as context["request"] which is great e.g. to serialize based on the request.user).

A common use case is to specify which fields to include or omit in the serialized data. There are 3rd party packages like this handy thing drf-dynamic-fields that provides a query_params based interface for this. Simply adding the mixin to a serializer will allow us to omit fields on the request level with urls like /api/resource?omit=field1,field2. It would be great to have a similar interface to omit fields directly on a serializer instance, wouldn't it? Sadly this is not supported out of the box.

We could build our own mixin that handles this based on the context, but I have also found a shortcut for the package mentioned above. Every DRF Request wraps the original Django HTTPRequest. Its GET parameters are immutable, but we can copy and reassign them to make them editable.

Doing so allows us to reuse the query_params interface and so something like this to dynamically include/exclude fields e.g. to simply save some serialization time when we don't need that field information:

# api/serializers.py
from drf_dynamic_fields import DynamicFieldsMixin

class MyModelSerializer(DynamicFieldsMixin,
                        serializers.ModelSerializer):
    fields = ("field1", "field2", "field3", "field4",)

# api/views.py
def list_my_model(request):
    # Do as if the user requested '/api/mymodel?omit=field1,field2'
    # See: https://github.com/dbrgn/drf-dynamic-fields/blob/master/drf_dynamic_fields/__init__.py#L45  # noqa
    request._request.GET = request._request.GET.copy()
    request._request.GET["omit"] = "field1,field2"

    data = MyModel.objects.filter(...)
    context = {"request": request}
    serializer = MyModelSerializer(data, context=context)
    return Response(serializer.data)
i_dont_know_what_i_want