From 3f3213b5d04149cdf852a36397f4626502862182 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 10 Oct 2016 12:51:19 +0100 Subject: [PATCH] Renamed and documented 'get_schema_fields' interface. --- docs/api-guide/filtering.md | 6 ++++++ docs/api-guide/pagination.md | 6 ++++++ rest_framework/filters.py | 28 +++++++++++++++++++--------- rest_framework/pagination.py | 34 ++++++++++++++++++++++++---------- rest_framework/schemas.py | 15 ++------------- 5 files changed, 57 insertions(+), 32 deletions(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 0ccd51dd3..9c3f28895 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -416,6 +416,12 @@ Generic filters may also present an interface in the browsable API. To do so you The method should return a rendered HTML string. +## Pagination & schemas + +You can also make the filter controls available to the schema autogeneration +that REST framework provides, by implementing a `get_schema_fields()` method, +which should return a list of `coreapi.Field` instances. + # Third party packages The following third party packages provide additional filter implementations. diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 088e07184..f990128c5 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -276,6 +276,12 @@ To have your custom pagination class be used by default, use the `DEFAULT_PAGINA API responses for list endpoints will now include a `Link` header, instead of including the pagination links as part of the body of the response, for example: +## Pagination & schemas + +You can also make the pagination controls available to the schema autogeneration +that REST framework provides, by implementing a `get_schema_fields()` method, +which should return a list of `coreapi.Field` instances. + --- ![Link Header][link-header] diff --git a/rest_framework/filters.py b/rest_framework/filters.py index aaf313491..27167adc4 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -16,7 +16,7 @@ from django.utils import six from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import ( - crispy_forms, distinct, django_filters, guardian, template_render + coreapi, crispy_forms, distinct, django_filters, guardian, template_render ) from rest_framework.settings import api_settings @@ -72,7 +72,8 @@ class BaseFilterBackend(object): """ raise NotImplementedError(".filter_queryset() must be overridden.") - def get_fields(self, view): + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' return [] @@ -131,14 +132,21 @@ class DjangoFilterBackend(BaseFilterBackend): template = loader.get_template(self.template) return template_render(template, context) - def get_fields(self, view): + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' filter_class = getattr(view, 'filter_class', None) if filter_class: - return list(filter_class().filters.keys()) + return [ + coreapi.Field(name=field_name, required=False, location='query') + for field_name in filter_class().filters.keys() + ] filter_fields = getattr(view, 'filter_fields', None) if filter_fields: - return filter_fields + return [ + coreapi.Field(name=field_name, required=False, location='query') + for field_name in filter_fields + ] return [] @@ -231,8 +239,9 @@ class SearchFilter(BaseFilterBackend): template = loader.get_template(self.template) return template_render(template, context) - def get_fields(self, view): - return [self.search_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [coreapi.Field(name=self.search_param, required=False, location='query')] class OrderingFilter(BaseFilterBackend): @@ -347,8 +356,9 @@ class OrderingFilter(BaseFilterBackend): context = self.get_template_context(request, queryset, view) return template_render(template, context) - def get_fields(self, view): - return [self.ordering_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [coreapi.Field(name=self.ordering_param, required=False, location='query')] class DjangoObjectPermissionsFilter(BaseFilterBackend): diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index c54894efc..6b539b900 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -15,7 +15,7 @@ from django.utils import six from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import template_render +from rest_framework.compat import coreapi, template_render from rest_framework.exceptions import NotFound from rest_framework.response import Response from rest_framework.settings import api_settings @@ -157,7 +157,8 @@ class BasePagination(object): def get_results(self, data): return data['results'] - def get_fields(self, view): + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' return [] @@ -283,10 +284,16 @@ class PageNumberPagination(BasePagination): context = self.get_html_context() return template_render(template, context) - def get_fields(self, view): - if self.page_size_query_param is None: - return [self.page_query_param] - return [self.page_query_param, self.page_size_query_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + fields = [ + coreapi.Field(name=self.page_query_param, required=False, location='query') + ] + if self.page_size_query_param is not None: + fields.append([ + coreapi.Field(name=self.page_size_query_param, required=False, location='query') + ]) + return fields class LimitOffsetPagination(BasePagination): @@ -415,8 +422,12 @@ class LimitOffsetPagination(BasePagination): context = self.get_html_context() return template_render(template, context) - def get_fields(self, view): - return [self.limit_query_param, self.offset_query_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [ + coreapi.Field(name=self.limit_query_param, required=False, location='query'), + coreapi.Field(name=self.offset_query_param, required=False, location='query') + ] class CursorPagination(BasePagination): @@ -721,5 +732,8 @@ class CursorPagination(BasePagination): context = self.get_html_context() return template_render(template, context) - def get_fields(self, view): - return [self.cursor_query_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [ + coreapi.Field(name=self.cursor_query_param, required=False, location='query') + ] diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 12119adfc..69698db0e 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -42,17 +42,6 @@ def get_pk_name(model): return _get_pk(meta).name -def as_query_fields(items): - """ - Take a list of Fields and plain strings. - Convert any pain strings into `location='query'` Field instances. - """ - return [ - item if isinstance(item, coreapi.Field) else coreapi.Field(name=item, required=False, location='query') - for item in items - ] - - def is_api_view(callback): """ Return `True` if the given view callback is a REST framework view/viewset. @@ -506,7 +495,7 @@ class SchemaGenerator(object): return [] paginator = view.pagination_class() - return as_query_fields(paginator.get_fields(view)) + return paginator.get_schema_fields(view) def get_filter_fields(self, path, method, view): if not is_list_view(path, method, view): @@ -517,7 +506,7 @@ class SchemaGenerator(object): fields = [] for filter_backend in view.filter_backends: - fields += as_query_fields(filter_backend().get_fields(view)) + fields += filter_backend().get_schema_fields(view) return fields # Method for generating the link layout....