Django Rest Framework: Dynamically return subset of fields Django Rest Framework: Dynamically return subset of fields django django

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