From c98e52d80d8deb626fe5f4826cf2872e79a86459 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 15 Jul 2019 15:18:59 -0400 Subject: [PATCH] Added get_schema_operation_parameters() for DRF OpenAPI schema generation. (#1086) * dummy get_schema_operation_parameters() so DRF generateschema won't throw an exception. * implement get_schema_operation_parameters() * Updated release note text. --- CHANGES.rst | 5 +++++ django_filters/rest_framework/backends.py | 22 ++++++++++++++++++++++ docs/guide/rest_framework.txt | 6 +++--- tests/rest_framework/test_backends.py | 9 +++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 59d216a4a..45cf24974 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,8 @@ +Version 2.x (unreleased) +------------------------ + +* Added ``DjangoFilterBackend.get_schema_operation_parameters()`` for DRF 3.10+ OpenAPI schema generation. + Version 2.1 (2019-1-20) ----------------------- diff --git a/django_filters/rest_framework/backends.py b/django_filters/rest_framework/backends.py index f13035adb..eed802a41 100644 --- a/django_filters/rest_framework/backends.py +++ b/django_filters/rest_framework/backends.py @@ -138,3 +138,25 @@ def get_schema_fields(self, view): schema=self.get_coreschema_field(field) ) for field_name, field in filterset_class.base_filters.items() ] + + def get_schema_operation_parameters(self, view): + 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': field_name, + '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() + ] diff --git a/docs/guide/rest_framework.txt b/docs/guide/rest_framework.txt index 69e336edd..2384049c1 100644 --- a/docs/guide/rest_framework.txt +++ b/docs/guide/rest_framework.txt @@ -148,10 +148,10 @@ You can override these methods on a case-by-case basis for each view, creating u 'author': self.get_author(), } -Schema Generation with Core API -------------------------------- +Schema Generation with Core API and Open API +-------------------------------------------- -The backend class integrates with DRF's schema generation by implementing ``get_schema_fields()``. This is automatically enabled when Core API is installed. Schema generation usually functions seamlessly, however the implementation does expect to invoke the view's ``get_queryset()`` method. There is a caveat in that views are artificially constructed during schema generation, so the ``args`` and ``kwargs`` attributes will be empty. If you depend on arguments parsed from the URL, you will need to handle their absence in ``get_queryset()``. +The backend class integrates with DRF's schema generation by implementing ``get_schema_fields()`` and ``get_schema_operation_parameters()``. ``get_schema_fields()`` is automatically enabled when Core API is installed. ``get_schema_operation_parameters()`` is always enabled for Open API (new since DRF 3.9). Schema generation usually functions seamlessly, however the implementation does expect to invoke the view's ``get_queryset()`` method. There is a caveat in that views are artificially constructed during schema generation, so the ``args`` and ``kwargs`` attributes will be empty. If you depend on arguments parsed from the URL, you will need to handle their absence in ``get_queryset()``. For example, your get queryset method may look like this: diff --git a/tests/rest_framework/test_backends.py b/tests/rest_framework/test_backends.py index cf5abe841..429ccfc3a 100644 --- a/tests/rest_framework/test_backends.py +++ b/tests/rest_framework/test_backends.py @@ -229,6 +229,15 @@ class View(FilterClassRootView): self.assertEqual(fields, ['text', 'decimal', 'date', 'f']) +class GetSchemaOperationParametersTests(TestCase): + def test_get_operation_parameters_with_filterset_fields_list(self): + backend = DjangoFilterBackend() + fields = backend.get_schema_operation_parameters(FilterFieldsRootView()) + fields = [f['name'] for f in fields] + + self.assertEqual(fields, ['decimal', 'date']) + + class TemplateTests(TestCase): def test_backend_output(self): """