96 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
Mixin to dynamically select only a subset of fields per DRF resource.
 | 
						|
"""
 | 
						|
# import warnings
 | 
						|
from django_filters import rest_framework as filters
 | 
						|
 | 
						|
# from django.conf import settings
 | 
						|
class DynamicFieldsFilterMixin(object):
 | 
						|
    fields = filters.CharFilter(method='filter_fields')
 | 
						|
    omit = filters.CharFilter(method='filter_omit')
 | 
						|
    def filter_fields(self, queryset, name, value):
 | 
						|
        return queryset
 | 
						|
 | 
						|
    def filter_omit(self, queryset, name, value):
 | 
						|
        return queryset
 | 
						|
 | 
						|
    @property
 | 
						|
    def fields(self):
 | 
						|
        fields = super(DynamicFieldsFilterMixin, self).fields
 | 
						|
        fields.extend(['fields', 'omit'])
 | 
						|
        return fields
 | 
						|
 | 
						|
class DynamicFieldsSerializerMixin(object):
 | 
						|
    """
 | 
						|
    A serializer mixin that takes an additional `fields` argument that controls
 | 
						|
    which fields should be displayed.
 | 
						|
    """
 | 
						|
 | 
						|
    @property
 | 
						|
    def fields(self):
 | 
						|
        """
 | 
						|
        Filters the fields according to the `fields` query parameter.
 | 
						|
        A blank `fields` parameter (?fields) will remove all fields. Not
 | 
						|
        passing `fields` will pass all fields individual fields are comma
 | 
						|
        separated (?fields=id,name,url,email).
 | 
						|
        """
 | 
						|
        fields = super(DynamicFieldsSerializerMixin, self).fields
 | 
						|
 | 
						|
        if not hasattr(self, '_context'):
 | 
						|
            # We are being called before a request cycle
 | 
						|
            return fields
 | 
						|
 | 
						|
        # Only filter if this is the root serializer, or if the parent is the
 | 
						|
        # root serializer with many=True
 | 
						|
        is_root = self.root == self
 | 
						|
        parent_is_list_root = self.parent == self.root and getattr(self.parent, 'many', False)
 | 
						|
        if not (is_root or parent_is_list_root):
 | 
						|
            return fields
 | 
						|
 | 
						|
        try:
 | 
						|
            request = self.context['request']
 | 
						|
        except KeyError:
 | 
						|
            # conf = getattr(settings, 'DRF_DYNAMIC_FIELDS', {})
 | 
						|
            # if not conf.get('SUPPRESS_CONTEXT_WARNING', False) is True:
 | 
						|
            #     warnings.warn('Context does not have access to request. '
 | 
						|
            #                   'See README for more information.')
 | 
						|
            return fields
 | 
						|
 | 
						|
        # NOTE: drf test framework builds a request object where the query
 | 
						|
        # parameters are found under the GET attribute.
 | 
						|
        params = getattr(
 | 
						|
            request, 'query_params', getattr(request, 'GET', None)
 | 
						|
        )
 | 
						|
        # if params is None:
 | 
						|
        #     warnings.warn('Request object does not contain query paramters')
 | 
						|
 | 
						|
        try:
 | 
						|
            filter_fields = params.get('fields', None).split(',')
 | 
						|
        except AttributeError:
 | 
						|
            filter_fields = None
 | 
						|
 | 
						|
        try:
 | 
						|
            omit_fields = params.get('omit', None).split(',')
 | 
						|
        except AttributeError:
 | 
						|
            omit_fields = []
 | 
						|
 | 
						|
        # Drop any fields that are not specified in the `fields` argument.
 | 
						|
        existing = set(fields.keys())
 | 
						|
        if filter_fields is None:
 | 
						|
            # no fields param given, don't filter.
 | 
						|
            allowed = existing
 | 
						|
        else:
 | 
						|
            allowed = set(filter(None, filter_fields))
 | 
						|
 | 
						|
        # omit fields in the `omit` argument.
 | 
						|
        omitted = set(filter(None, omit_fields))
 | 
						|
 | 
						|
        for field in existing:
 | 
						|
 | 
						|
            if field not in allowed:
 | 
						|
                fields.pop(field, None)
 | 
						|
 | 
						|
            if field in omitted:
 | 
						|
                fields.pop(field, None)
 | 
						|
 | 
						|
        return fields |