Django Rest Framework: Dynamically return subset of fields
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 serializersclass 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')
This functionality is available from a 3rd-party package.
pip install djangorestframework-queryfields
Declare your serializer like this:
from rest_framework.serializers import ModelSerializerfrom drf_queryfields import QueryFieldsMixinclass MyModelSerializer(QueryFieldsMixin, ModelSerializer): ...
Then the fields can now be specified (client-side) by using query arguments:
GET /identities/?fields=id,data
Exclusion filtering is also possible, e.g. to return every field except id:
GET /identities/?fields!=id
disclaimer: I'm the author/maintainer.
serializers.py
class DynamicFieldsSerializerMixin(object): def __init__(self, *args, **kwargs): # Don't pass the 'fields' arg up to the superclass fields = kwargs.pop('fields', None) # Instantiate the superclass normally super(DynamicFieldsSerializerMixin, self).__init__(*args, **kwargs) if fields is not None: # 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(DynamicFieldsSerializerMixin, serializers.HyperlinkedModelSerializer): password = serializers.CharField( style={'input_type': 'password'}, write_only=True ) class Meta: model = User fields = ('id', 'username', 'password', 'email', 'first_name', 'last_name') def create(self, validated_data): user = User.objects.create( username=validated_data['username'], email=validated_data['email'], first_name=validated_data['first_name'], last_name=validated_data['last_name'] ) user.set_password(validated_data['password']) user.save() return user
views.py
class DynamicFieldsViewMixin(object): def get_serializer(self, *args, **kwargs): serializer_class = self.get_serializer_class() fields = None if self.request.method == 'GET': query_fields = self.request.QUERY_PARAMS.get("fields", None) if query_fields: fields = tuple(query_fields.split(',')) kwargs['context'] = self.get_serializer_context() kwargs['fields'] = fields return serializer_class(*args, **kwargs)class UserList(DynamicFieldsViewMixin, ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer