From cd826ce422b2bbf18a675c50b989f51e253e229e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 5 Oct 2016 13:46:39 +0100 Subject: [PATCH] Added 'get_schema_view()' shortcut --- docs/api-guide/schemas.md | 77 ++++++++++--------- .../7-schemas-and-client-libraries.md | 21 +++-- rest_framework/schemas.py | 52 ++++++++++--- 3 files changed, 94 insertions(+), 56 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 7f8af723e..57b73addc 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -102,15 +102,20 @@ REST framework includes functionality for auto-generating a schema, or allows you to specify one explicitly. There are a few different ways to add a schema to your API, depending on exactly what you need. -## Using DefaultRouter +## The get_schema_view shortcut -If you're using `DefaultRouter` then you can include an auto-generated schema, -simply by adding a `schema_title` argument to the router. +The simplest way to include a schema in your project is to use the +`get_schema_view()` function. - router = DefaultRouter(schema_title='Server Monitoring API') + schema_view = get_schema_view(title="Server Monitoring API") -The schema will be included at the root URL, `/`, and presented to clients -that include the Core JSON media type in their `Accept` header. + urlpatterns = [ + url('^$', schema_view), + ... + ] + +Once the view has been added, you'll be able to make API requests to retrieve +the auto-generated schema definition. $ http http://127.0.0.1:8000/ Accept:application/vnd.coreapi+json HTTP/1.0 200 OK @@ -125,48 +130,43 @@ that include the Core JSON media type in their `Accept` header. ... } -This is a great zero-configuration option for when you want to get up and -running really quickly. +The arguments to `get_schema_view()` are: -The other available options to `DefaultRouter` are: +#### `title` -#### schema_renderers +May be used to provide a descriptive title for the schema definition. -May be used to pass the set of renderer classes that can be used to render schema output. +#### `url` + +May be used to pass a canonical URL for the schema. + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/' + ) + +#### `renderer_classes` + +May be used to pass the set of renderer classes that can be used to render the API root endpoint. from rest_framework.renderers import CoreJSONRenderer from my_custom_package import APIBlueprintRenderer - router = DefaultRouter(schema_title='Server Monitoring API', schema_renderers=[ - CoreJSONRenderer, APIBlueprintRenderer - ]) - -#### schema_url - -May be used to pass the root URL for the schema. This can either be used to ensure that -the schema URLs include a canonical hostname and schema, or to ensure that all the -schema URLs include a path prefix. - - router = DefaultRouter( - schema_title='Server Monitoring API', - schema_url='https://www.example.org/api/' + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + renderer_classes=[CoreJSONRenderer, APIBlueprintRenderer] ) -If you want more flexibility over the schema output then you'll need to consider -using `SchemaGenerator` instead. +## Using an explicit schema view -#### root_renderers - -May be used to pass the set of renderer classes that can be used to render the API root endpoint. - -## Using SchemaGenerator - -The most common way to add a schema to your API is to use the `SchemaGenerator` -class to auto-generate the `Document` instance, and to return that from a view. +If you need a little more control than the `get_schema_view()` shortcut gives you, +then you can use the `SchemaGenerator` class directly to auto-generate the +`Document` instance, and to return that from a view. This option gives you the flexibility of setting up the schema endpoint -with whatever behavior you want. For example, you can apply different -permission, throttling or authentication policies to the schema endpoint. +with whatever behaviour you want. For example, you can apply different +permission, throttling, or authentication policies to the schema endpoint. Here's an example of using `SchemaGenerator` together with a view to return the schema. @@ -176,12 +176,13 @@ return the schema. from rest_framework.decorators import api_view, renderer_classes from rest_framework import renderers, response, schemas + generator = schemas.SchemaGenerator(title='Bookings API') @api_view() @renderer_classes([renderers.CoreJSONRenderer]) def schema_view(request): - generator = schemas.SchemaGenerator(title='Bookings API') - return response.Response(generator.get_schema()) + schema = generator.get_schema(request) + return response.Response(schema) **urls.py:** diff --git a/docs/tutorial/7-schemas-and-client-libraries.md b/docs/tutorial/7-schemas-and-client-libraries.md index 77cfdd3b3..3d4d2c941 100644 --- a/docs/tutorial/7-schemas-and-client-libraries.md +++ b/docs/tutorial/7-schemas-and-client-libraries.md @@ -33,10 +33,17 @@ API schema. $ pip install coreapi -We can now include a schema for our API, by adding a `schema_title` argument to -the router instantiation. +We can now include a schema for our API, by including an autogenerated schema +view in our URL configuration. - router = DefaultRouter(schema_title='Pastebin API') + from rest_framework.schemas import get_schema_view + + schema_view = get_schema_view(title='Pastebin API') + + urlpatterns = [ + url('^schema/$', schema_view), + ... + ] If you visit the API root endpoint in a browser you should now see `corejson` representation become available as an option. @@ -46,7 +53,7 @@ representation become available as an option. We can also request the schema from the command line, by specifying the desired content type in the `Accept` header. - $ http http://127.0.0.1:8000/ Accept:application/vnd.coreapi+json + $ http http://127.0.0.1:8000/schema/ Accept:application/vnd.coreapi+json HTTP/1.0 200 OK Allow: GET, HEAD, OPTIONS Content-Type: application/vnd.coreapi+json @@ -91,8 +98,8 @@ Now check that it is available on the command line... First we'll load the API schema using the command line client. - $ coreapi get http://127.0.0.1:8000/ - + $ coreapi get http://127.0.0.1:8000/schema/ + snippets: { highlight(pk) list() @@ -150,7 +157,7 @@ Now if we fetch the schema again, we should be able to see the full set of available interactions. $ coreapi reload - Pastebin API "http://127.0.0.1:8000/"> + Pastebin API "http://127.0.0.1:8000/schema/"> snippets: { create(code, [title], [linenos], [language], [style]) destroy(pk) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index c3b36cd1e..d3c9d5025 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -6,11 +6,13 @@ from django.contrib.admindocs.views import simplify_regex from django.utils import six from django.utils.encoding import force_text -from rest_framework import exceptions, serializers +from rest_framework import exceptions, renderers, serializers from rest_framework.compat import ( RegexURLPattern, RegexURLResolver, coreapi, uritemplate, urlparse ) from rest_framework.request import clone_request +from rest_framework.response import Response +from rest_framework.settings import api_settings from rest_framework.views import APIView @@ -92,15 +94,14 @@ class EndpointInspector(object): if patterns is None: if urlconf is None: # Use the default Django URL conf - urls = import_module(settings.ROOT_URLCONF) - patterns = urls.urlpatterns + urlconf = settings.ROOT_URLCONF + + # Load the given URLconf module + if isinstance(urlconf, six.string_types): + urls = import_module(urlconf) else: - # Load the given URLconf module - if isinstance(urlconf, six.string_types): - urls = import_module(urlconf) - else: - urls = urlconf - patterns = urls.urlpatterns + urls = urlconf + patterns = urls.urlpatterns self.patterns = patterns @@ -189,7 +190,8 @@ class SchemaGenerator(object): if url and not url.endswith('/'): url += '/' - self.endpoint_inspector = self.endpoint_inspector_cls(patterns, urlconf) + self.patterns = patterns + self.urlconf = urlconf self.title = title self.url = url self.endpoints = None @@ -199,7 +201,8 @@ class SchemaGenerator(object): Generate a `coreapi.Document` representing the API schema. """ if self.endpoints is None: - self.endpoints = self.endpoint_inspector.get_api_endpoints() + inspector = self.endpoint_inspector_cls(self.patterns, self.urlconf) + self.endpoints = inspector.get_api_endpoints() links = self.get_links(request) if not links: @@ -425,3 +428,30 @@ class SchemaGenerator(object): # Default action, eg "/users/", "/users/{pk}/" return named_path_components + [action] + + +def get_schema_view(title=None, url=None, renderer_classes=None): + """ + Return a schema view. + """ + generator = SchemaGenerator(title=title, url=url) + if renderer_classes is None: + if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES: + rclasses = [renderers.CoreJSONRenderer, renderers.BrowsableAPIRenderer] + else: + 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) + if schema is None: + raise exceptions.PermissionDenied() + return Response(schema) + + return SchemaView.as_view()