diff --git a/README.rst b/README.rst index b2c71e9..a7e1294 100644 --- a/README.rst +++ b/README.rst @@ -137,6 +137,15 @@ pass in the request through the context: serializer = EventSerializer(events, many=True, context={'request': request}) +In addition to using query parameters to pass ``omit`` and ``fields``, the +serializer context can be used as well (also taking precedence over the query): + +.. sourcecode:: python + + events = Event.objects.all() + context = {'request': request, 'omit': 'id,name'} + serializer = EventSerializer(events, many=True, context=context) + Testing ------- diff --git a/drf_dynamic_fields/__init__.py b/drf_dynamic_fields/__init__.py index e19124d..ec67922 100644 --- a/drf_dynamic_fields/__init__.py +++ b/drf_dynamic_fields/__init__.py @@ -10,6 +10,27 @@ class DynamicFieldsMixin(object): which fields should be displayed. """ + def _collect_params(self, request): + """Params can be passed via the GET query or the serializer context. + + Both sources are merged into a unique set, with the context + taking predecence over query parameters. + + """ + params = getattr( + request, 'query_params', getattr(request, 'GET', None) + ) + + if params is not None: + params = params.copy() + + for param_name in ('omit', 'fields'): + param_value = self.context.get(param_name, None) + if param_value is not None: + params[param_name] = param_value + + return params + @property def fields(self): """ @@ -29,7 +50,8 @@ def fields(self): # 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) + 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 @@ -39,13 +61,10 @@ def fields(self): warnings.warn('Context does not have access to request') 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) - ) + params = self._collect_params(request) + if params is None: - warnings.warn('Request object does not contain query paramters') + warnings.warn('Request object does not contain query parameters') try: filter_fields = params.get('fields', None).split(',') diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 6b7718a..c540c7f 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -169,3 +169,51 @@ def test_as_nested_serializer(self): ], } ) + + def test_pass_params_via_serializer_context(self): + """ + Params can solely (or additionally) be passed via serializer context. + """ + rf = RequestFactory() + request = rf.get('/api/v1/schools/1/') + context = { + 'request': request, + 'omit': 'request_info', + } + serializer = TeacherSerializer(context=context) + + self.assertEqual( + set(serializer.fields.keys()), + set(('id',)) + ) + + def test_serializer_context_overwrites_query_params(self): + """ + Params passed via context take precedence over query_params. + """ + rf = RequestFactory() + request = rf.get('/api/v1/schools/1/?fields=request_info') + context = { + 'request': request, + 'fields': 'id', + } + serializer = TeacherSerializer(context=context) + + self.assertEqual( + set(serializer.fields.keys()), + set(('id',)) + ) + + def test_no_params_passed_at_all(self): + """ + Specifying neither query_params nor context params should not fail. + """ + rf = RequestFactory() + request = rf.get('/api/v1/schools/1/') + context = {'request': request} + serializer = TeacherSerializer(context=context) + + self.assertEqual( + set(serializer.fields.keys()), + set(('id', 'request_info')) + )