Django Rest Framework: Dynamically return subset of fields

后端 未结 8 706
南笙
南笙 2020-11-28 18:16

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 R

8条回答
  •  南方客
    南方客 (楼主)
    2020-11-28 18:56

    Configure a new pagination serializer class

    from rest_framework import pagination, serializers
    
    class DynamicFieldsPaginationSerializer(pagination.BasePaginationSerializer):
        """
        A dynamic fields implementation of a pagination serializer.
        """
        count = serializers.Field(source='paginator.count')
        next = pagination.NextPageField(source='*')
        previous = pagination.PreviousPageField(source='*')
    
        def __init__(self, *args, **kwargs):
            """
            Override init to add in the object serializer field on-the-fly.
            """
            fields = kwargs.pop('fields', None)
            super(pagination.BasePaginationSerializer, self).__init__(*args, **kwargs)
            results_field = self.results_field
            object_serializer = self.opts.object_serializer_class
    
            if 'context' in kwargs:
                context_kwarg = {'context': kwargs['context']}
            else:
                context_kwarg = {}
    
            if fields:
                context_kwarg.update({'fields': fields})
    
            self.fields[results_field] = object_serializer(source='object_list',
                                                           many=True,
                                                           **context_kwarg)
    
    
    # Set the pagination serializer setting
    REST_FRAMEWORK = {
        # [...]
        'DEFAULT_PAGINATION_SERIALIZER_CLASS': 'DynamicFieldsPaginationSerializer',
    }
    

    Make dynamic serializer

    from rest_framework import serializers
    
    class DynamicFieldsModelSerializer(serializers.ModelSerializer):
        """
        A ModelSerializer that takes an additional `fields` argument that
        controls which fields should be displayed.
    
        See:
            http://tomchristie.github.io/rest-framework-2-docs/api-guide/serializers
        """
    
        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(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
    
            if fields:
                # 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)
    # Use it
    class MyPonySerializer(DynamicFieldsModelSerializer):
        # [...]
    

    Last, use a homemage mixin for your APIViews

    class DynamicFields(object):
        """A mixins that allows the query builder to display certain fields"""
    
        def get_fields_to_display(self):
            fields = self.request.GET.get('fields', None)
            return fields.split(',') if fields else None
    
        def get_serializer(self, instance=None, data=None, files=None, many=False,
                           partial=False, allow_add_remove=False):
            """
            Return the serializer instance that should be used for validating and
            deserializing input, and for serializing output.
            """
            serializer_class = self.get_serializer_class()
            context = self.get_serializer_context()
            fields = self.get_fields_to_display()
            return serializer_class(instance, data=data, files=files,
                                    many=many, partial=partial,
                                    allow_add_remove=allow_add_remove,
                                    context=context, fields=fields)
    
        def get_pagination_serializer(self, page):
            """
            Return a serializer instance to use with paginated data.
            """
            class SerializerClass(self.pagination_serializer_class):
                class Meta:
                    object_serializer_class = self.get_serializer_class()
    
            pagination_serializer_class = SerializerClass
            context = self.get_serializer_context()
            fields = self.get_fields_to_display()
            return pagination_serializer_class(instance=page, context=context, fields=fields)
    
    class MyPonyList(DynamicFields, generics.ListAPIView):
        # [...]
    

    Request

    Now, when you request a resource, you can add a parameter fields to show only specified fields in url. /?fields=field1,field2

    You can find a reminder here : https://gist.github.com/Kmaschta/e28cf21fb3f0b90c597a

提交回复
热议问题