exclude_from_schema

This commit is contained in:
Tom Christie 2016-10-05 12:42:52 +01:00
parent 8044d38c21
commit a8501f72b7
5 changed files with 41 additions and 25 deletions

View File

@ -127,7 +127,7 @@ REST framework also allows you to work with regular function based views. It pr
## @api_view()
**Signature:** `@api_view(http_method_names=['GET'])`
**Signature:** `@api_view(http_method_names=['GET'], exclude_from_schema=False)`
The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data:
@ -139,7 +139,7 @@ The core of this functionality is the `api_view` decorator, which takes a list o
This view will use the default renderers, parsers, authentication classes etc specified in the [settings].
By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behavior, specify which methods the view allows, like so:
By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behaviour, specify which methods the view allows, like so:
@api_view(['GET', 'POST'])
def hello_world(request):
@ -147,6 +147,13 @@ By default only `GET` methods will be accepted. Other methods will respond with
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})
You can also mark an API view as being omitted from any [auto-generated schema][schemas],
using the `exclude_from_schema` argument.:
@api_view(['GET'], exclude_from_schema=True)
def api_docs(request):
...
## API policy decorators
To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle][throttling] to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes:
@ -178,3 +185,4 @@ Each of these decorators takes a single argument which must be a list or tuple o
[cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html
[settings]: settings.md
[throttling]: throttling.md
[schemas]: schemas.md

View File

@ -15,7 +15,7 @@ from django.utils import six
from rest_framework.views import APIView
def api_view(http_method_names=None):
def api_view(http_method_names=None, exclude_from_schema=False):
"""
Decorator that converts a function-based view into an APIView subclass.
Takes a list of allowed methods for the view as an argument.
@ -72,6 +72,7 @@ def api_view(http_method_names=None):
WrappedAPIView.permission_classes = getattr(func, 'permission_classes',
APIView.permission_classes)
WrappedAPIView.exclude_from_schema = exclude_from_schema
return WrappedAPIView.as_view()
return decorator

View File

@ -311,6 +311,7 @@ class DefaultRouter(SimpleRouter):
class APISchemaView(views.APIView):
_ignore_model_permissions = True
exclude_from_schema = True
renderer_classes = schema_renderers
def get(self, request, *args, **kwargs):
@ -332,6 +333,7 @@ class DefaultRouter(SimpleRouter):
class APIRootView(views.APIView):
_ignore_model_permissions = True
exclude_from_schema = True
def get(self, request, *args, **kwargs):
# Return a plain {"name": "hyperlink"} response.

View File

@ -56,6 +56,22 @@ def is_custom_action(action):
])
def is_list_view(path, method, view):
"""
Return True if the given path/method appears to represent a list view.
"""
if hasattr(view, 'action'):
# Viewsets have an explicitly defined action, which we can inspect.
return view.action == 'list'
if method.lower() != 'get':
return False
path_components = path.strip('/').split('/')
if path_components and '{' in path_components[-1]:
return False
return True
def endpoint_ordering(endpoint):
path, method, callback = endpoint
method_priority = {
@ -136,9 +152,6 @@ class EndpointInspector(object):
if path.endswith('.{format}') or path.endswith('.{format}/'):
return False # Ignore .json style URLs.
if path == '/':
return False # Ignore the root endpoint.
return True
def get_allowed_methods(self, callback):
@ -201,7 +214,7 @@ class SchemaGenerator(object):
links = OrderedDict()
for path, method, callback in self.endpoints:
view = self.create_view(callback, method, request)
if not self.has_view_permissions(view):
if not self.should_include_view(path, method, view):
continue
link = self.get_link(path, method, view)
keys = self.get_keys(path, method, view)
@ -235,10 +248,13 @@ class SchemaGenerator(object):
return view
def has_view_permissions(self, view):
def should_include_view(self, path, method, view):
"""
Return `True` if the incoming request has the correct view permissions.
"""
if getattr(view, 'exclude_from_schema', False):
return False
if view.request is None:
return True
@ -248,20 +264,6 @@ class SchemaGenerator(object):
return False
return True
def is_list_endpoint(self, path, method, view):
"""
Return True if the given path/method appears to represent a list endpoint.
"""
if hasattr(view, 'action'):
return view.action == 'list'
if method.lower() != 'get':
return False
path_components = path.strip('/').split('/')
if path_components and '{' in path_components[-1]:
return False
return True
# Methods for generating each individual `Link` instance...
def get_link(self, path, method, view):
@ -359,7 +361,7 @@ class SchemaGenerator(object):
return fields
def get_pagination_fields(self, path, method, view):
if not self.is_list_endpoint(path, method, view):
if not is_list_view(path, method, view):
return []
if not getattr(view, 'pagination_class', None):
@ -369,7 +371,7 @@ class SchemaGenerator(object):
return as_query_fields(paginator.get_fields(view))
def get_filter_fields(self, path, method, view):
if not self.is_list_endpoint(path, method, view):
if not is_list_view(path, method, view):
return []
if not getattr(view, 'filter_backends', None):
@ -402,7 +404,7 @@ class SchemaGenerator(object):
action = view.action
else:
# Views have no associated action, so we determine one from the method.
if self.is_list_endpoint(path, method, view):
if is_list_view(path, method, view):
action = 'list'
else:
action = self.default_mapping[method.lower()]

View File

@ -110,6 +110,9 @@ class APIView(View):
# Allow dependency injection of other settings to make testing easier.
settings = api_settings
# Mark the view as being included or excluded from schema generation.
exclude_from_schema = False
@classmethod
def as_view(cls, **initkwargs):
"""