Django Rest Framework: Dynamically return subset of fields
django, django-rest-framework
Solution
You can override the serializer `__init__` method and set the `fields` attribute dynamically, based on the query params. You can access the `request` object throughout the context, passed to the serializer.
Here is a copy&paste from Django Rest Framework documentation example on the matter:
from rest_framework import serializers
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Instantiate the superclass normally
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
fields = self.context['request'].query_params.get('fields')
if fields:
fields = fields.split(',')
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields.keys())
for field_name in existing - allowed:
self.fields.pop(field_name)
class UserSerializer(DynamicFieldsModelSerializer, serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email')
Problem
Problem As recommended in the blogpost Best Practices for Designing a Pragmatic RESTful API, I would like to add a `fields` query parameter to a Django Rest Framework based API which enables the user to select only a subset of fields per resource. Example Serializer: ``` class IdentitySerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.Identity fields = ('id', 'url', 'type', 'data') ``` A regular query would return all fields. `GET /identities/` ``` [ { "id": 1, "url": "http://localhost:8000/api/identities/1/", "type": 5, "data": "John Doe" }, ... ] ``` A query with the `fields` parameter should only return a subset of the fields: `GET /identities/?fields=id,data` ``` [ { "id": 1, "data": "John Doe" }, ... ] ``` A query with invalid fields should either ignore the invalid fields or throw a client error. Goal Is this possible out of the box somehow? If not, what's the simplest way to implement this? Is there a 3rd party package around that does this already?