Extract APISchemaView and APIRootView out of the DefaultRouter. (#4707)

This commit is contained in:
Xavier Ordoquy 2017-03-07 14:39:08 +01:00 committed by Tom Christie
parent cf5d401a0e
commit 537df7a6ad
2 changed files with 78 additions and 63 deletions

View File

@ -22,12 +22,11 @@ from collections import OrderedDict, namedtuple
from django.conf.urls import url from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from rest_framework import exceptions, renderers, views from rest_framework import views
from rest_framework.compat import NoReverseMatch from rest_framework.compat import NoReverseMatch
from rest_framework.renderers import BrowsableAPIRenderer
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.schemas import SchemaGenerator from rest_framework.schemas import SchemaGenerator, SchemaView
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.urlpatterns import format_suffix_patterns from rest_framework.urlpatterns import format_suffix_patterns
@ -276,78 +275,19 @@ class SimpleRouter(BaseRouter):
return ret return ret
class DefaultRouter(SimpleRouter): class APIRootView(views.APIView):
""" """
The default router extends the SimpleRouter, but also adds in a default The default basic root view for DefaultRouter
API root view, and adds format suffix patterns to the URLs.
""" """
include_root_view = True
include_format_suffixes = True
root_view_name = 'api-root'
default_schema_renderers = [renderers.CoreJSONRenderer, BrowsableAPIRenderer]
def __init__(self, *args, **kwargs):
if 'schema_title' in kwargs:
warnings.warn(
"Including a schema directly via a router is now pending "
"deprecation. Use `get_schema_view()` instead.",
PendingDeprecationWarning
)
if 'schema_renderers' in kwargs:
assert 'schema_title' in kwargs, 'Missing "schema_title" argument.'
if 'schema_url' in kwargs:
assert 'schema_title' in kwargs, 'Missing "schema_title" argument.'
self.schema_title = kwargs.pop('schema_title', None)
self.schema_url = kwargs.pop('schema_url', None)
self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers)
if 'root_renderers' in kwargs:
self.root_renderers = kwargs.pop('root_renderers')
else:
self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
super(DefaultRouter, self).__init__(*args, **kwargs)
def get_schema_root_view(self, api_urls=None):
"""
Return a schema root view.
"""
schema_renderers = self.schema_renderers
schema_generator = SchemaGenerator(
title=self.schema_title,
url=self.schema_url,
patterns=api_urls
)
class APISchemaView(views.APIView):
_ignore_model_permissions = True
exclude_from_schema = True
renderer_classes = schema_renderers
def get(self, request, *args, **kwargs):
schema = schema_generator.get_schema(request)
if schema is None:
raise exceptions.PermissionDenied()
return Response(schema)
return APISchemaView.as_view()
def get_api_root_view(self, api_urls=None):
"""
Return a basic root view.
"""
api_root_dict = OrderedDict()
list_name = self.routes[0].name
for prefix, viewset, basename in self.registry:
api_root_dict[prefix] = list_name.format(basename=basename)
class APIRootView(views.APIView):
_ignore_model_permissions = True _ignore_model_permissions = True
exclude_from_schema = True exclude_from_schema = True
api_root_dict = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
# Return a plain {"name": "hyperlink"} response. # Return a plain {"name": "hyperlink"} response.
ret = OrderedDict() ret = OrderedDict()
namespace = request.resolver_match.namespace namespace = request.resolver_match.namespace
for key, url_name in api_root_dict.items(): for key, url_name in self.api_root_dict.items():
if namespace: if namespace:
url_name = namespace + ':' + url_name url_name = namespace + ':' + url_name
try: try:
@ -364,7 +304,72 @@ class DefaultRouter(SimpleRouter):
return Response(ret) return Response(ret)
return APIRootView.as_view()
class DefaultRouter(SimpleRouter):
"""
The default router extends the SimpleRouter, but also adds in a default
API root view, and adds format suffix patterns to the URLs.
"""
include_root_view = True
include_format_suffixes = True
root_view_name = 'api-root'
default_schema_renderers = None
APIRootView = APIRootView
APISchemaView = SchemaView
def __init__(self, *args, **kwargs):
if 'schema_title' in kwargs:
warnings.warn(
"Including a schema directly via a router is now pending "
"deprecation. Use `get_schema_view()` instead.",
PendingDeprecationWarning
)
if 'schema_renderers' in kwargs:
assert 'schema_title' in kwargs, 'Missing "schema_title" argument.'
if 'schema_url' in kwargs:
assert 'schema_title' in kwargs, 'Missing "schema_title" argument.'
self.schema_title = kwargs.pop('schema_title', None)
self.schema_url = kwargs.pop('schema_url', None)
self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers)
if self.default_schema_renderers:
warnings.warn(
"The 'DefaultRouter.default_schema_renderers' is pending "
"deprecation. You should override "
"'DefaultRouter.APISchemaView' instead.",
PendingDeprecationWarning
)
if 'root_renderers' in kwargs:
self.root_renderers = kwargs.pop('root_renderers')
else:
self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
super(DefaultRouter, self).__init__(*args, **kwargs)
def get_schema_root_view(self, api_urls=None):
"""
Return a schema root view.
"""
schema_generator = SchemaGenerator(
title=self.schema_title,
url=self.schema_url,
patterns=api_urls
)
return self.APISchemaView.as_view(
renderer_classes=self.schema_renderers,
schema_generator=schema_generator,
)
def get_api_root_view(self, api_urls=None):
"""
Return a basic root view.
"""
api_root_dict = OrderedDict()
list_name = self.routes[0].name
for prefix, viewset, basename in self.registry:
api_root_dict[prefix] = list_name.format(basename=basename)
return self.APIRootView.as_view(api_root_dict=api_root_dict)
def get_urls(self): def get_urls(self):
""" """

View File

@ -665,28 +665,38 @@ class SchemaGenerator(object):
return named_path_components + [action] return named_path_components + [action]
class SchemaView(APIView):
_ignore_model_permissions = True
exclude_from_schema = True
renderer_classes = None
schema_generator = None
public = False
def __init__(self, *args, **kwargs):
super(SchemaView, self).__init__(*args, **kwargs)
if self.renderer_classes is None:
if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES:
self.renderer_classes = [
renderers.CoreJSONRenderer,
renderers.BrowsableAPIRenderer,
]
else:
self.renderer_classes = [renderers.CoreJSONRenderer]
def get(self, request, *args, **kwargs):
schema = self.schema_generator.get_schema(request, self.public)
if schema is None:
raise exceptions.PermissionDenied()
return Response(schema)
def get_schema_view(title=None, url=None, description=None, urlconf=None, renderer_classes=None, public=False): def get_schema_view(title=None, url=None, description=None, urlconf=None, renderer_classes=None, public=False):
""" """
Return a schema view. Return a schema view.
""" """
generator = SchemaGenerator(title=title, url=url, description=description, urlconf=urlconf) generator = SchemaGenerator(title=title, url=url, description=description, urlconf=urlconf)
if renderer_classes is None: return SchemaView.as_view(
if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES: renderer_classes=renderer_classes,
rclasses = [renderers.CoreJSONRenderer, renderers.BrowsableAPIRenderer] schema_generator=generator,
else: public=public,
rclasses = [renderers.CoreJSONRenderer] )
else:
rclasses = renderer_classes
class SchemaView(APIView):
_ignore_model_permissions = True
exclude_from_schema = True
renderer_classes = rclasses
def get(self, request, *args, **kwargs):
schema = generator.get_schema(request, public)
if schema is None:
raise exceptions.PermissionDenied()
return Response(schema)
return SchemaView.as_view()