From 817e49c1b9ea34c6aaebe9186e3b51195552d550 Mon Sep 17 00:00:00 2001 From: Christoph Buelter Date: Tue, 26 Sep 2017 13:43:18 +0200 Subject: [PATCH 1/2] Use the serializer context as optional params source. This allows passing omit/fields values to drf-dynamic-fields from a lower level API, useful when creating serializers manually, e.g. inside other serializers. --- drf_dynamic_fields/__init__.py | 33 ++++++++++++++++++----- tests/test_mixins.py | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 7 deletions(-) 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')) + ) From 4323232fe71392a5e0f0554092b794b46161045d Mon Sep 17 00:00:00 2001 From: Christoph Buelter Date: Tue, 26 Sep 2017 13:51:07 +0200 Subject: [PATCH 2/2] Update README about using serializer context to pass parameters. --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) 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 -------