From 828e59ccc99d8c2751460c5c8155dba20972b257 Mon Sep 17 00:00:00 2001 From: Alan Crosswell <alan@columbia.edu> Date: Fri, 31 May 2019 16:40:45 -0400 Subject: [PATCH 1/4] implement DjangoFilterBackend.get_schema_operation_parameters --- .../django_filters/backends.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rest_framework_json_api/django_filters/backends.py b/rest_framework_json_api/django_filters/backends.py index 347acb58..58ac1a75 100644 --- a/rest_framework_json_api/django_filters/backends.py +++ b/rest_framework_json_api/django_filters/backends.py @@ -1,4 +1,5 @@ import re +import warnings from django_filters import VERSION from django_filters.rest_framework import DjangoFilterBackend @@ -142,3 +143,31 @@ def filter_queryset(self, request, queryset, view): return filter_class(kwargs['data'], queryset=queryset, request=request).qs return queryset + + def get_schema_operation_parameters(self, view): + """ + Return Open API query parameter schema. + """ + # TODO: Update this to extend the upstream django-filter implemntation if and when that gets merged. + # see https://github.com/carltongibson/django-filter/pull/1086 + + try: + queryset = view.get_queryset() + except Exception: + queryset = None + warnings.warn( + "{} is not compatible with schema generation".format(view.__class__) + ) + + filterset_class = self.get_filterset_class(view, queryset) + return [] if not filterset_class else [ + ({ + 'name': 'filter[{}]'.format(field_name.replace('__', '.')), + 'required': field.extra['required'], + 'in': 'query', + 'description': field.label if field.label is not None else field_name, + 'schema': { + 'type': 'string', + }, + }) for field_name, field in filterset_class.base_filters.items() + ] From bf77ff2348549c69d0e26e2fd0ae1cf396a47e48 Mon Sep 17 00:00:00 2001 From: Alan Crosswell <alan@columbia.edu> Date: Sun, 9 Jun 2019 18:49:26 -0400 Subject: [PATCH 2/4] test case for get_schema_operation_parameters --- example/tests/unit/test_openapi.py | 60 ++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 example/tests/unit/test_openapi.py diff --git a/example/tests/unit/test_openapi.py b/example/tests/unit/test_openapi.py new file mode 100644 index 00000000..78aa5cd4 --- /dev/null +++ b/example/tests/unit/test_openapi.py @@ -0,0 +1,60 @@ +from rest_framework import filters as drf_filters +from rest_framework_json_api import filters as dja_filters +from rest_framework_json_api.django_filters import backends +from example.views import EntryViewSet + + +class DummyEntryViewSet(EntryViewSet): + filter_backends = (dja_filters.QueryParameterValidationFilter, dja_filters.OrderingFilter, + backends.DjangoFilterBackend, drf_filters.SearchFilter) + filterset_fields = { + 'id': ('exact',), + 'headline': ('exact',), + } + +def test_filters_get_schema_params(): + """ + test all my filters for `get_schema_operation_parameters()` + """ + # list of tuples: (filter, expected result) + filters = [ + (dja_filters.QueryParameterValidationFilter, []), + (backends.DjangoFilterBackend, + [ + { + 'name': 'filter[id]', 'required': False, 'in': 'query', + 'description': 'id', 'schema': {'type': 'string'} + }, + { + 'name': 'filter[headline]', 'required': False, 'in': 'query', + 'description': 'headline', 'schema': {'type': 'string'} + } + ] + ), + (dja_filters.OrderingFilter, + [ + { + 'name': 'sort', 'required': False, 'in': 'query', + 'description': 'Which field to use when ordering the results.', + 'schema': {'type': 'string'} + } + ] + ), + (drf_filters.SearchFilter, + [ + { + 'name': 'filter[search]', 'required': False, 'in': 'query', + 'description': 'A search term.', + 'schema': {'type': 'string'} + } + ] + ), + ] + view = DummyEntryViewSet() + + for c, expected in filters: + f = c() + # get_schema_operation_parameters is only available in DRF >= 3.10 + if hasattr(f, 'get_schema_operation_parameters'): + result = f.get_schema_operation_parameters(view) + assert result == expected From d400c4ad65d21db8c73c68dd0ccf8e80c15588b0 Mon Sep 17 00:00:00 2001 From: Alan Crosswell <alan@columbia.edu> Date: Sun, 9 Jun 2019 18:50:00 -0400 Subject: [PATCH 3/4] confusion about view.queryset vs. view.get_queryset() --- rest_framework_json_api/django_filters/backends.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_framework_json_api/django_filters/backends.py b/rest_framework_json_api/django_filters/backends.py index 58ac1a75..3c04baf7 100644 --- a/rest_framework_json_api/django_filters/backends.py +++ b/rest_framework_json_api/django_filters/backends.py @@ -152,7 +152,8 @@ def get_schema_operation_parameters(self, view): # see https://github.com/carltongibson/django-filter/pull/1086 try: - queryset = view.get_queryset() + #queryset = view.get_queryset() + queryset = view.queryset except Exception: queryset = None warnings.warn( From c7afeb7af3ff6f689144289ca9791a9d0a5dd9fc Mon Sep 17 00:00:00 2001 From: Alan Crosswell <alan@columbia.edu> Date: Sun, 9 Jun 2019 18:57:57 -0400 Subject: [PATCH 4/4] flake8 --- example/tests/unit/test_openapi.py | 57 +++++++++---------- .../django_filters/backends.py | 5 +- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/example/tests/unit/test_openapi.py b/example/tests/unit/test_openapi.py index 78aa5cd4..01217eb5 100644 --- a/example/tests/unit/test_openapi.py +++ b/example/tests/unit/test_openapi.py @@ -1,6 +1,8 @@ from rest_framework import filters as drf_filters + from rest_framework_json_api import filters as dja_filters from rest_framework_json_api.django_filters import backends + from example.views import EntryViewSet @@ -12,6 +14,7 @@ class DummyEntryViewSet(EntryViewSet): 'headline': ('exact',), } + def test_filters_get_schema_params(): """ test all my filters for `get_schema_operation_parameters()` @@ -19,36 +22,30 @@ def test_filters_get_schema_params(): # list of tuples: (filter, expected result) filters = [ (dja_filters.QueryParameterValidationFilter, []), - (backends.DjangoFilterBackend, - [ - { - 'name': 'filter[id]', 'required': False, 'in': 'query', - 'description': 'id', 'schema': {'type': 'string'} - }, - { - 'name': 'filter[headline]', 'required': False, 'in': 'query', - 'description': 'headline', 'schema': {'type': 'string'} - } - ] - ), - (dja_filters.OrderingFilter, - [ - { - 'name': 'sort', 'required': False, 'in': 'query', - 'description': 'Which field to use when ordering the results.', - 'schema': {'type': 'string'} - } - ] - ), - (drf_filters.SearchFilter, - [ - { - 'name': 'filter[search]', 'required': False, 'in': 'query', - 'description': 'A search term.', - 'schema': {'type': 'string'} - } - ] - ), + (backends.DjangoFilterBackend, [ + { + 'name': 'filter[id]', 'required': False, 'in': 'query', + 'description': 'id', 'schema': {'type': 'string'} + }, + { + 'name': 'filter[headline]', 'required': False, 'in': 'query', + 'description': 'headline', 'schema': {'type': 'string'} + } + ]), + (dja_filters.OrderingFilter, [ + { + 'name': 'sort', 'required': False, 'in': 'query', + 'description': 'Which field to use when ordering the results.', + 'schema': {'type': 'string'} + } + ]), + (drf_filters.SearchFilter, [ + { + 'name': 'filter[search]', 'required': False, 'in': 'query', + 'description': 'A search term.', + 'schema': {'type': 'string'} + } + ]), ] view = DummyEntryViewSet() diff --git a/rest_framework_json_api/django_filters/backends.py b/rest_framework_json_api/django_filters/backends.py index 3c04baf7..88211831 100644 --- a/rest_framework_json_api/django_filters/backends.py +++ b/rest_framework_json_api/django_filters/backends.py @@ -148,11 +148,8 @@ def get_schema_operation_parameters(self, view): """ Return Open API query parameter schema. """ - # TODO: Update this to extend the upstream django-filter implemntation if and when that gets merged. - # see https://github.com/carltongibson/django-filter/pull/1086 - try: - #queryset = view.get_queryset() + # queryset = view.get_queryset() queryset = view.queryset except Exception: queryset = None