diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 94332c0ed..cc53e9c80 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -3,7 +3,7 @@ source: - schemas.py --- -# Schemas +# Schema > A machine-readable [schema] describes what resources are available via the API, what their URLs are, how they are represented and what operations they support. > @@ -13,24 +13,24 @@ API schemas are a useful tool that allow for a range of use cases, including generating reference documentation, or driving dynamic client libraries that can interact with your API. -## Install Core API & PyYAML +Django REST Framework provides support for automatic generation of +[OpenAPI][openapi] schemas. -You'll need to install the `coreapi` package in order to add schema support -for REST framework. You probably also want to install `pyyaml`, so that you -can render the schema into the commonly used YAML-based OpenAPI format. +## Generating an OpenAPI Schema - pip install coreapi pyyaml +### Install `pyyaml` -## Quickstart +You'll need to install `pyyaml`, so that you can render your generated schema +into the commonly used YAML-based OpenAPI format. -There are two different ways you can serve a schema description for your API. + pip install pyyaml -### Generating a schema with the `generateschema` management command +### Generating a static schema with the `generateschema` management command -To generate a static API schema, use the `generateschema` management command. +If your schema is static, you can use the `generateschema` management command: -```shell -$ python manage.py generateschema > schema.yml +```bash +./manage.py generateschema > openapi-schema.yml ``` Once you've generated a schema in this way you can annotate it with any @@ -40,154 +40,133 @@ generator. You might want to check your API schema into version control and update it with each new release, or serve the API schema from your site's static media. -### Adding a view with `get_schema_view` +### Generating a dynamic schema with `SchemaView` -To add a dynamically generated schema view to your API, use `get_schema_view`. +If you require a dynamic schema, because foreign key choices depend on database +values, for example, you can route a `SchemaView` that will generate and serve +your schema on demand. + +To route a `SchemaView`, use the `get_schema_view()` helper. + +In `urls.py`: ```python -from rest_framework.schemas import get_schema_view - -schema_view = get_schema_view(title="Example API") +from rest_framework.schemas import get_schema_view() urlpatterns = [ - url('^schema$', schema_view), - ... + # ... + # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs. + # * `title` and `description` parameters are passed to `SchemaGenerator`. + # * Provide view name for use with `reverse()`. + path('openapi', get_schema_view( + title="Your Project", + description="API for all things …" + ), name='openapi-schema'), + # ... ] ``` -See below [for more details](#the-get_schema_view-shortcut) on customizing a -dynamically generated schema view. +#### `get_schema_view()` -## Internal schema representation +The `get_schema_view()` helper takes the following keyword arguments: -REST framework uses [Core API][coreapi] in order to model schema information in -a format-independent representation. This information can then be rendered -into various different schema formats, or used to generate API documentation. +* `title`: May be used to provide a descriptive title for the schema definition. +* `description`: Longer descriptive text. +* `url`: May be used to pass a canonical base URL for the schema. -When using Core API, a schema is represented as a `Document` which is the -top-level container object for information about the API. Available API -interactions are represented using `Link` objects. Each link includes a URL, -HTTP method, and may include a list of `Field` instances, which describe any -parameters that may be accepted by the API endpoint. The `Link` and `Field` -instances may also include descriptions, that allow an API schema to be -rendered into user documentation. + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/' + ) -Here's an example of an API description that includes a single `search` -endpoint: +* `urlconf`: A string representing the import path to the URL conf that you want + to generate an API schema for. This defaults to the value of Django's + `ROOT_URLCONF` setting. - coreapi.Document( - title='Flight Search API', - url='https://api.example.org/', - content={ - 'search': coreapi.Link( - url='/search/', - action='get', - fields=[ - coreapi.Field( - name='from', - required=True, - location='query', - description='City name or airport code.' - ), - coreapi.Field( - name='to', - required=True, - location='query', - description='City name or airport code.' - ), - coreapi.Field( - name='date', - required=True, - location='query', - description='Flight date in "YYYY-MM-DD" format.' - ) - ], - description='Return flight availability and prices.' - ) - } - ) + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + urlconf='myproject.urls' + ) +* `patterns`: List of url patterns to limit the schema introspection to. If you + only want the `myproject.api` urls to be exposed in the schema: -## Schema output formats + schema_url_patterns = [ + url(r'^api/', include('myproject.api.urls')), + ] -In order to be presented in an HTTP response, the internal representation -has to be rendered into the actual bytes that are used in the response. + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + patterns=schema_url_patterns, + ) -REST framework includes a few different renderers that you can use for -encoding the API schema. - -* `renderers.OpenAPIRenderer` - Renders into YAML-based [OpenAPI][open-api], the most widely used API schema format. -* `renderers.JSONOpenAPIRenderer` - Renders into JSON-based [OpenAPI][open-api]. -* `renderers.CoreJSONRenderer` - Renders into [Core JSON][corejson], a format designed for -use with the `coreapi` client library. +* `generator_class`: May be used to specify a `SchemaGenerator` subclass to be + passed to the `SchemaView`. +* `authentication_classes`: May be used to specify the list of authentication + classes that will apply to the schema endpoint. Defaults to + `settings.DEFAULT_AUTHENTICATION_CLASSES` +* `permission_classes`: May be used to specify the list of permission classes + that will apply to the schema endpoint. Defaults to + `settings.DEFAULT_PERMISSION_CLASSES`. +* `renderer_classes`: May be used to pass the set of renderer classes that can + be used to render the API root endpoint. -[Core JSON][corejson] is designed as a canonical format for use with Core API. -REST framework includes a renderer class for handling this media type, which -is available as `renderers.CoreJSONRenderer`. +## Customizing Schema Generation +You may customize schema generation at the level of the schema as a whole, or +on a per-view basis. -## Schemas vs Hypermedia +### Schema Level Customization -It's worth pointing out here that Core API can also be used to model hypermedia -responses, which present an alternative interaction style to API schemas. +In order to customize the top-level schema sublass +`rest_framework.schemas.openapi.SchemaGenerator` and provide it as an argument +to the `generateschema` command or `get_schema_view()` helper function. -With an API schema, the entire available interface is presented up-front -as a single endpoint. Responses to individual API endpoints are then typically -presented as plain data, without any further interactions contained in each -response. +#### SchemaGenerator -With Hypermedia, the client is instead presented with a document containing -both data and available interactions. Each interaction results in a new -document, detailing both the current state and the available interactions. +A class that walks a list of routed URL patterns, requests the schema for each +view and collates the resulting OpenAPI schema. -Further information and support on building Hypermedia APIs with REST framework -is planned for a future version. +Typically you'll instantiate `SchemaGenerator` with a `title` argument, like so: + generator = SchemaGenerator(title='Stock Prices API') ---- +Arguments: -# Creating a schema +* `title` **required**: The name of the API. +* `description`: Longer descriptive text. +* `url`: The root URL of the API schema. This option is not required unless the schema is included under path prefix. +* `patterns`: A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. +* `urlconf`: A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. -REST framework includes functionality for auto-generating a schema, -or allows you to specify one explicitly. +##### get_schema(self, request) -## Manual Schema Specification +Returns a dictionary that represents the OpenAPI schema: -To manually specify a schema you create a Core API `Document`, similar to the -example above. - - schema = coreapi.Document( - title='Flight Search API', - content={ - ... - } - ) - - -## Automatic Schema Generation - -Automatic schema generation is provided by the `SchemaGenerator` class. - -`SchemaGenerator` processes a list of routed URL patterns and compiles the -appropriately structured Core API Document. - -Basic usage is just to provide the title for your schema and call -`get_schema()`: - - generator = schemas.SchemaGenerator(title='Flight Search API') + generator = SchemaGenerator(title='Stock Prices API') schema = generator.get_schema() -## Per-View Schema Customisation +The `request` argument is optional, and may be used if you want to apply +per-user permissions to the resulting schema generation. + +This is a good point to override if you want to customise the generated +dictionary, for example to add custom +[specification extensions][openapi-specification-extensions]. + +### Per-View Customization By default, view introspection is performed by an `AutoSchema` instance accessible via the `schema` attribute on `APIView`. This provides the -appropriate Core API `Link` object for the view, request method and path: +appropriate [Open API operation object][openapi-operation] for the view, +request method and path: auto_schema = view.schema - coreapi_link = auto_schema.get_link(...) + operation = auto_schema.get_operation(...) -(In compiling the schema, `SchemaGenerator` calls `view.schema.get_link()` for -each view, allowed method and path.) +In compiling the schema, `SchemaGenerator` calls `view.schema.get_operation()` +for each view, allowed method, and path. --- @@ -201,641 +180,39 @@ provide richer path field descriptions. (The key hooks here are the relevant --- -To customise the `Link` generation you may: +In order to customise the operation generation, you should provide an `AutoSchema` subclass, overriding `get_operation()` as you need: -* Instantiate `AutoSchema` on your view with the `manual_fields` kwarg: from rest_framework.views import APIView - from rest_framework.schemas import AutoSchema - - class CustomView(APIView): - ... - schema = AutoSchema( - manual_fields=[ - coreapi.Field("extra_field", ...), - ] - ) - - This allows extension for the most common case without subclassing. - -* Provide an `AutoSchema` subclass with more complex customisation: - - from rest_framework.views import APIView - from rest_framework.schemas import AutoSchema + from rest_framework.schemas.openapi import AutoSchema class CustomSchema(AutoSchema): def get_link(...): # Implement custom introspection here (or in other sub-methods) class CustomView(APIView): - ... + """APIView subclass with custom schema introspection.""" schema = CustomSchema() - This provides complete control over view introspection. - -* Instantiate `ManualSchema` on your view, providing the Core API `Fields` for - the view explicitly: - - from rest_framework.views import APIView - from rest_framework.schemas import ManualSchema - - class CustomView(APIView): - ... - schema = ManualSchema(fields=[ - coreapi.Field( - "first_field", - required=True, - location="path", - schema=coreschema.String() - ), - coreapi.Field( - "second_field", - required=True, - location="path", - schema=coreschema.String() - ), - ]) - - This allows manually specifying the schema for some views whilst maintaining - automatic generation elsewhere. +This provides complete control over view introspection. You may disable schema generation for a view by setting `schema` to `None`: - class CustomView(APIView): - ... - schema = None # Will not appear in schema + class CustomView(APIView): + ... + schema = None # Will not appear in schema This also applies to extra actions for `ViewSet`s: - class CustomViewSet(viewsets.ModelViewSet): + class CustomViewSet(viewsets.ModelViewSet): - @action(detail=True, schema=None) - def extra_action(self, request, pk=None): - ... - ---- - -**Note**: For full details on `SchemaGenerator` plus the `AutoSchema` and -`ManualSchema` descriptors see the [API Reference below](#api-reference). - ---- - -# Adding a schema view - -There are a few different ways to add a schema view to your API, depending on -exactly what you need. - -## The get_schema_view shortcut - -The simplest way to include a schema in your project is to use the -`get_schema_view()` function. - - from rest_framework.schemas import get_schema_view - - schema_view = get_schema_view(title="Server Monitoring API") - - 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/coreapi+json - HTTP/1.0 200 OK - Allow: GET, HEAD, OPTIONS - Content-Type: application/vnd.coreapi+json - - { - "_meta": { - "title": "Server Monitoring API" - }, - "_type": "document", - ... - } - -The arguments to `get_schema_view()` are: - -#### `title` - -May be used to provide a descriptive title for the schema definition. - -#### `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/' - ) - -#### `urlconf` - -A string representing the import path to the URL conf that you want -to generate an API schema for. This defaults to the value of Django's -ROOT_URLCONF setting. - - schema_view = get_schema_view( - title='Server Monitoring API', - url='https://www.example.org/api/', - urlconf='myproject.urls' - ) - -#### `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.schemas import get_schema_view - from rest_framework.renderers import JSONOpenAPIRenderer - - schema_view = get_schema_view( - title='Server Monitoring API', - url='https://www.example.org/api/', - renderer_classes=[JSONOpenAPIRenderer] - ) - -#### `patterns` - -List of url patterns to limit the schema introspection to. If you only want the `myproject.api` urls -to be exposed in the schema: - - schema_url_patterns = [ - url(r'^api/', include('myproject.api.urls')), - ] - - schema_view = get_schema_view( - title='Server Monitoring API', - url='https://www.example.org/api/', - patterns=schema_url_patterns, - ) - -#### `generator_class` - -May be used to specify a `SchemaGenerator` subclass to be passed to the -`SchemaView`. - -#### `authentication_classes` - -May be used to specify the list of authentication classes that will apply to the schema endpoint. -Defaults to `settings.DEFAULT_AUTHENTICATION_CLASSES` - -#### `permission_classes` - -May be used to specify the list of permission classes that will apply to the schema endpoint. -Defaults to `settings.DEFAULT_PERMISSION_CLASSES` - -## Using an explicit schema 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 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. - -**views.py:** - - 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.OpenAPIRenderer]) - def schema_view(request): - schema = generator.get_schema(request) - return response.Response(schema) - -**urls.py:** - - urlpatterns = [ - url('/', schema_view), - ... - ] - -You can also serve different schemas to different users, depending on the -permissions they have available. This approach can be used to ensure that -unauthenticated requests are presented with a different schema to -authenticated requests, or to ensure that different parts of the API are -made visible to different users depending on their role. - -In order to present a schema with endpoints filtered by user permissions, -you need to pass the `request` argument to the `get_schema()` method, like so: - - @api_view() - @renderer_classes([renderers.OpenAPIRenderer]) - def schema_view(request): - generator = schemas.SchemaGenerator(title='Bookings API') - return response.Response(generator.get_schema(request=request)) - -## Explicit schema definition - -An alternative to the auto-generated approach is to specify the API schema -explicitly, by declaring a `Document` object in your codebase. Doing so is a -little more work, but ensures that you have full control over the schema -representation. - - import coreapi - from rest_framework.decorators import api_view, renderer_classes - from rest_framework import renderers, response - - schema = coreapi.Document( - title='Bookings API', - content={ + @action(detail=True, schema=None) + def extra_action(self, request, pk=None): ... - } - ) - @api_view() - @renderer_classes([renderers.OpenAPIRenderer]) - def schema_view(request): - return response.Response(schema) +If you wish to provide a base `AutoSchema` subclass to be used throughout your +project you may adjust `settings.DEFAULT_SCHEMA_CLASS` appropriately. ---- - -# Schemas as documentation - -One common usage of API schemas is to use them to build documentation pages. - -The schema generation in REST framework uses docstrings to automatically -populate descriptions in the schema document. - -These descriptions will be based on: - -* The corresponding method docstring if one exists. -* A named section within the class docstring, which can be either single line or multi-line. -* The class docstring. - -## Examples - -An `APIView`, with an explicit method docstring. - - class ListUsernames(APIView): - def get(self, request): - """ - Return a list of all user names in the system. - """ - usernames = [user.username for user in User.objects.all()] - return Response(usernames) - -A `ViewSet`, with an explict action docstring. - - class ListUsernames(ViewSet): - def list(self, request): - """ - Return a list of all user names in the system. - """ - usernames = [user.username for user in User.objects.all()] - return Response(usernames) - -A generic view with sections in the class docstring, using single-line style. - - class UserList(generics.ListCreateAPIView): - """ - get: List all the users. - post: Create a new user. - """ - queryset = User.objects.all() - serializer_class = UserSerializer - permission_classes = (IsAdminUser,) - -A generic viewset with sections in the class docstring, using multi-line style. - - class UserViewSet(viewsets.ModelViewSet): - """ - API endpoint that allows users to be viewed or edited. - - retrieve: - Return a user instance. - - list: - Return all users, ordered by most recently joined. - """ - queryset = User.objects.all().order_by('-date_joined') - serializer_class = UserSerializer - ---- - -# API Reference - -## SchemaGenerator - -A class that walks a list of routed URL patterns, requests the schema for each view, -and collates the resulting CoreAPI Document. - -Typically you'll instantiate `SchemaGenerator` with a single argument, like so: - - generator = SchemaGenerator(title='Stock Prices API') - -Arguments: - -* `title` **required** - The name of the API. -* `url` - The root URL of the API schema. This option is not required unless the schema is included under path prefix. -* `patterns` - A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. -* `urlconf` - A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. - -### get_schema(self, request) - -Returns a `coreapi.Document` instance that represents the API schema. - - @api_view - @renderer_classes([renderers.OpenAPIRenderer]) - def schema_view(request): - generator = schemas.SchemaGenerator(title='Bookings API') - return Response(generator.get_schema()) - -The `request` argument is optional, and may be used if you want to apply per-user -permissions to the resulting schema generation. - -### get_links(self, request) - -Return a nested dictionary containing all the links that should be included in the API schema. - -This is a good point to override if you want to modify the resulting structure of the generated schema, -as you can build a new dictionary with a different layout. - - -## AutoSchema - -A class that deals with introspection of individual views for schema generation. - -`AutoSchema` is attached to `APIView` via the `schema` attribute. - -The `AutoSchema` constructor takes a single keyword argument `manual_fields`. - -**`manual_fields`**: a `list` of `coreapi.Field` instances that will be added to -the generated fields. Generated fields with a matching `name` will be overwritten. - - class CustomView(APIView): - schema = AutoSchema(manual_fields=[ - coreapi.Field( - "my_extra_field", - required=True, - location="path", - schema=coreschema.String() - ), - ]) - -For more advanced customisation subclass `AutoSchema` to customise schema generation. - - class CustomViewSchema(AutoSchema): - """ - Overrides `get_link()` to provide Custom Behavior X - """ - - def get_link(self, path, method, base_url): - link = super().get_link(path, method, base_url) - # Do something to customize link here... - return link - - class MyView(APIView): - schema = CustomViewSchema() - -The following methods are available to override. - -### get_link(self, path, method, base_url) - -Returns a `coreapi.Link` instance corresponding to the given view. - -This is the main entry point. -You can override this if you need to provide custom behaviors for particular views. - -### get_description(self, path, method) - -Returns a string to use as the link description. By default this is based on the -view docstring as described in the "Schemas as Documentation" section above. - -### get_encoding(self, path, method) - -Returns a string to indicate the encoding for any request body, when interacting -with the given view. Eg. `'application/json'`. May return a blank string for views -that do not expect a request body. - -### get_path_fields(self, path, method): - -Return a list of `coreapi.Field()` instances. One for each path parameter in the URL. - -### get_serializer_fields(self, path, method) - -Return a list of `coreapi.Field()` instances. One for each field in the serializer class used by the view. - -### get_pagination_fields(self, path, method) - -Return a list of `coreapi.Field()` instances, as returned by the `get_schema_fields()` method on any pagination class used by the view. - -### get_filter_fields(self, path, method) - -Return a list of `coreapi.Field()` instances, as returned by the `get_schema_fields()` method of any filter classes used by the view. - -### get_manual_fields(self, path, method) - -Return a list of `coreapi.Field()` instances to be added to or replace generated fields. Defaults to (optional) `manual_fields` passed to `AutoSchema` constructor. - -May be overridden to customise manual fields by `path` or `method`. For example, a per-method adjustment may look like this: - -```python -def get_manual_fields(self, path, method): - """Example adding per-method fields.""" - - extra_fields = [] - if method=='GET': - extra_fields = # ... list of extra fields for GET ... - if method=='POST': - extra_fields = # ... list of extra fields for POST ... - - manual_fields = super().get_manual_fields(path, method) - return manual_fields + extra_fields -``` - -### update_fields(fields, update_with) - -Utility `staticmethod`. Encapsulates logic to add or replace fields from a list -by `Field.name`. May be overridden to adjust replacement criteria. - - -## ManualSchema - -Allows manually providing a list of `coreapi.Field` instances for the schema, -plus an optional description. - - class MyView(APIView): - schema = ManualSchema(fields=[ - coreapi.Field( - "first_field", - required=True, - location="path", - schema=coreschema.String() - ), - coreapi.Field( - "second_field", - required=True, - location="path", - schema=coreschema.String() - ), - ] - ) - -The `ManualSchema` constructor takes two arguments: - -**`fields`**: A list of `coreapi.Field` instances. Required. - -**`description`**: A string description. Optional. - -**`encoding`**: Default `None`. A string encoding, e.g `application/json`. Optional. - ---- - -## Core API - -This documentation gives a brief overview of the components within the `coreapi` -package that are used to represent an API schema. - -Note that these classes are imported from the `coreapi` package, rather than -from the `rest_framework` package. - -### Document - -Represents a container for the API schema. - -#### `title` - -A name for the API. - -#### `url` - -A canonical URL for the API. - -#### `content` - -A dictionary, containing the `Link` objects that the schema contains. - -In order to provide more structure to the schema, the `content` dictionary -may be nested, typically to a second level. For example: - - content={ - "bookings": { - "list": Link(...), - "create": Link(...), - ... - }, - "venues": { - "list": Link(...), - ... - }, - ... - } - -### Link - -Represents an individual API endpoint. - -#### `url` - -The URL of the endpoint. May be a URI template, such as `/users/{username}/`. - -#### `action` - -The HTTP method associated with the endpoint. Note that URLs that support -more than one HTTP method, should correspond to a single `Link` for each. - -#### `fields` - -A list of `Field` instances, describing the available parameters on the input. - -#### `description` - -A short description of the meaning and intended usage of the endpoint. - -### Field - -Represents a single input parameter on a given API endpoint. - -#### `name` - -A descriptive name for the input. - -#### `required` - -A boolean, indicated if the client is required to included a value, or if -the parameter can be omitted. - -#### `location` - -Determines how the information is encoded into the request. Should be one of -the following strings: - -**"path"** - -Included in a templated URI. For example a `url` value of `/products/{product_code}/` could be used together with a `"path"` field, to handle API inputs in a URL path such as `/products/slim-fit-jeans/`. - -These fields will normally correspond with [named arguments in the project URL conf][named-arguments]. - -**"query"** - -Included as a URL query parameter. For example `?search=sale`. Typically for `GET` requests. - -These fields will normally correspond with pagination and filtering controls on a view. - -**"form"** - -Included in the request body, as a single item of a JSON object or HTML form. For example `{"colour": "blue", ...}`. Typically for `POST`, `PUT` and `PATCH` requests. Multiple `"form"` fields may be included on a single link. - -These fields will normally correspond with serializer fields on a view. - -**"body"** - -Included as the complete request body. Typically for `POST`, `PUT` and `PATCH` requests. No more than one `"body"` field may exist on a link. May not be used together with `"form"` fields. - -These fields will normally correspond with views that use `ListSerializer` to validate the request input, or with file upload views. - -#### `encoding` - -**"application/json"** - -JSON encoded request content. Corresponds to views using `JSONParser`. -Valid only if either one or more `location="form"` fields, or a single -`location="body"` field is included on the `Link`. - -**"multipart/form-data"** - -Multipart encoded request content. Corresponds to views using `MultiPartParser`. -Valid only if one or more `location="form"` fields is included on the `Link`. - -**"application/x-www-form-urlencoded"** - -URL encoded request content. Corresponds to views using `FormParser`. Valid -only if one or more `location="form"` fields is included on the `Link`. - -**"application/octet-stream"** - -Binary upload request content. Corresponds to views using `FileUploadParser`. -Valid only if a `location="body"` field is included on the `Link`. - -#### `description` - -A short description of the meaning and intended usage of the input field. - - ---- - -# Third party packages - -## drf-yasg - Yet Another Swagger Generator - -[drf-yasg][drf-yasg] generates [OpenAPI][open-api] documents suitable for code generation - nested schemas, -named models, response bodies, enum/pattern/min/max validators, form parameters, etc. - -[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api -[coreapi]: https://www.coreapi.org/ -[corejson]: https://www.coreapi.org/specification/encoding/#core-json-encoding -[drf-yasg]: https://github.com/axnsan12/drf-yasg/ -[open-api]: https://openapis.org/ -[json-hyperschema]: https://json-schema.org/latest/json-schema-hypermedia.html -[api-blueprint]: https://apiblueprint.org/ -[static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/ -[named-arguments]: https://docs.djangoproject.com/en/stable/topics/http/urls/#named-groups +[openapi]: https://github.com/OAI/OpenAPI-Specification +[openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions +[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject \ No newline at end of file diff --git a/docs/community/3.10-announcement.md b/docs/community/3.10-announcement.md new file mode 100644 index 000000000..7197eb591 --- /dev/null +++ b/docs/community/3.10-announcement.md @@ -0,0 +1,104 @@ + +# Django REST framework 3.10 + + +* Reworked OpenAPI schema generation. +* Python 3 only. + + +## OpenAPI Schema Generation. + +Since we first introduced schema support in Django REST Framework 3.5, OpenAPI has emerged as the widely adopted standard for modelling Web APIs. + +This release deprecates the old CoreAPI based schema generation, and introduces improved OpenAPI schema generation in its place. + +---- + +**Switching mode between `CoreAPI` and `OpenAPI`** + +Both the `generateschema` management command and `get_schema_view()` helper +function will automatically switch between `CoreAPI` and `OpenAPI` modes, +depending on the value of `api_settings.DEFAULT_SCHEMA_CLASS`. + +If `api_settings.DEFAULT_SCHEMA_CLASS` is a subclass of +`rest_framework.schemas.coreapi.AutoSchema` then `CoreAPI` mode will be +selected. Otherwise the new `OpenAPI` will apply. + +This means that, unless you previously overrode +`api_settings.DEFAULT_SCHEMA_CLASS`, you automatically be opted-in to the new +`OpenAPI` based schemas. + +You can continue to use CoreAPI schemas by setting the appropriate default +schema class: + +```python +# In settings.py +REST_FRAMEWORK = { + 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', +} +``` + +---- + +### Quickstart + +To get going with `OpenAPI` schemas, use the `get_schema_view()` shortcut. + +In your `urls.py`: + +```python +from rest_framework.schemas import get_schema_view() + +urlpatterns = [ + # ... + # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs. + # * `title` and `description` parameters are passed to `SchemaGenerator`. + # * Provide view name for use with `reverse()`. + path('openapi', get_schema_view( + title="Your Project", + description="API for all things …" + ), name='openapi-schema'), + # ... +] +``` + +See the Schemas documentation for more details. + +### Feature Roadmap + +For v3.7 (with `CoreAPI`) we tried to anticipate customizations that people +were likely to need. (Introducing `manual_fields` and `ManaualSchema`, for +example.) These were under-utilised. They weren't the right abstractions. + +So, for a fresh start with `OpenAPI`, customizing schema generation has two +simple rules: + +* Subclass `SchemaGenerator` for schema-level cusomizations. +* Subclass `AutoSchema` for view-level customizations. + +We'll wait to see what subclasses people actually come up with, for the +customizations they actually need, before trying to bring that back into the +core framework. + +There are two kinds of changes that easily predictable: + +* General improvements which fill in gaps in the automatic schema generation. +* More use-case specific adjustments, which adjust the API of `SchemaGenerator` + or `AutoSchema` + +We'll aim to bring the first type of change quickly in point releases. For the +second kind we'd like to adopt a slower approach, to make sure we keep the API +simple, and as widely applicable as possible, before we bring in API changes. + +We trust that approach makes sense. + +### Deprecating CoreAPI Schema Generation. + +The in-built docs that were introduced in Django REST Framework v3.5 were built on CoreAPI. These are now deprecated. You may continue to use them but they will be **removed in Django REST Framework v 3.12**. + +You should migrate to using the new OpenAPI based schema generation as soon as you can. + +We have removed the old documentation for the CoreAPI based schema generation. +You may view the [Legacy CoreAPI documentation here][legacy-core-api-docs]. + +[legacy-core-api-docs]:https://github.com/encode/django-rest-framework/blob/master/docs/coreapi/index.md diff --git a/docs/tutorial/7-schemas-and-client-libraries.md b/docs/coreapi/7-schemas-and-client-libraries.md similarity index 100% rename from docs/tutorial/7-schemas-and-client-libraries.md rename to docs/coreapi/7-schemas-and-client-libraries.md diff --git a/docs/coreapi/from-documenting-your-api.md b/docs/coreapi/from-documenting-your-api.md new file mode 100644 index 000000000..9ac3be686 --- /dev/null +++ b/docs/coreapi/from-documenting-your-api.md @@ -0,0 +1,171 @@ + +## Built-in API documentation + +The built-in API documentation includes: + +* Documentation of API endpoints. +* Automatically generated code samples for each of the available API client libraries. +* Support for API interaction. + +### Installation + +The `coreapi` library is required as a dependency for the API docs. Make sure +to install the latest version. The `Pygments` and `Markdown` libraries +are optional but recommended. + +To install the API documentation, you'll need to include it in your project's URLconf: + + from rest_framework.documentation import include_docs_urls + + urlpatterns = [ + ... + url(r'^docs/', include_docs_urls(title='My API title')) + ] + +This will include two different views: + + * `/docs/` - The documentation page itself. + * `/docs/schema.js` - A JavaScript resource that exposes the API schema. + +--- + +**Note**: By default `include_docs_urls` configures the underlying `SchemaView` to generate _public_ schemas. +This means that views will not be instantiated with a `request` instance. i.e. Inside the view `self.request` will be `None`. + +To be compatible with this behaviour, methods (such as `get_serializer` or `get_serializer_class` etc.) which inspect `self.request` or, particularly, `self.request.user` may need to be adjusted to handle this case. + +You may ensure views are given a `request` instance by calling `include_docs_urls` with `public=False`: + + from rest_framework.documentation import include_docs_urls + + urlpatterns = [ + ... + # Generate schema with valid `request` instance: + url(r'^docs/', include_docs_urls(title='My API title', public=False)) + ] + + +--- + + +### Documenting your views + +You can document your views by including docstrings that describe each of the available actions. +For example: + + class UserList(generics.ListAPIView): + """ + Return a list of all the existing users. + """ + +If a view supports multiple methods, you should split your documentation using `method:` style delimiters. + + class UserList(generics.ListCreateAPIView): + """ + get: + Return a list of all the existing users. + + post: + Create a new user instance. + """ + +When using viewsets, you should use the relevant action names as delimiters. + + class UserViewSet(viewsets.ModelViewSet): + """ + retrieve: + Return the given user. + + list: + Return a list of all the existing users. + + create: + Create a new user instance. + """ + +Custom actions on viewsets can also be documented in a similar way using the method names +as delimiters or by attaching the documentation to action mapping methods. + + class UserViewSet(viewsets.ModelViewset): + ... + + @action(detail=False, methods=['get', 'post']) + def some_action(self, request, *args, **kwargs): + """ + get: + A description of the get method on the custom action. + + post: + A description of the post method on the custom action. + """ + + @some_action.mapping.put + def put_some_action(): + """ + A description of the put method on the custom action. + """ + + +### `documentation` API Reference + +The `rest_framework.documentation` module provides three helper functions to help configure the interactive API documentation, `include_docs_urls` (usage shown above), `get_docs_view` and `get_schemajs_view`. + + `include_docs_urls` employs `get_docs_view` and `get_schemajs_view` to generate the url patterns for the documentation page and JavaScript resource that exposes the API schema respectively. They expose the following options for customisation. (`get_docs_view` and `get_schemajs_view` ultimately call `rest_frameworks.schemas.get_schema_view()`, see the Schemas docs for more options there.) + +#### `include_docs_urls` + +* `title`: Default `None`. May be used to provide a descriptive title for the schema definition. +* `description`: Default `None`. May be used to provide a description for the schema definition. +* `schema_url`: Default `None`. May be used to pass a canonical base URL for the schema. +* `public`: Default `True`. Should the schema be considered _public_? If `True` schema is generated without a `request` instance being passed to views. +* `patterns`: Default `None`. A list of URLs to inspect when generating the schema. If `None` project's URL conf will be used. +* `generator_class`: Default `rest_framework.schemas.SchemaGenerator`. May be used to specify a `SchemaGenerator` subclass to be passed to the `SchemaView`. +* `authentication_classes`: Default `api_settings.DEFAULT_AUTHENTICATION_CLASSES`. May be used to pass custom authentication classes to the `SchemaView`. +* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES` May be used to pass custom permission classes to the `SchemaView`. +* `renderer_classes`: Default `None`. May be used to pass custom renderer classes to the `SchemaView`. + +#### `get_docs_view` + +* `title`: Default `None`. May be used to provide a descriptive title for the schema definition. +* `description`: Default `None`. May be used to provide a description for the schema definition. +* `schema_url`: Default `None`. May be used to pass a canonical base URL for the schema. +* `public`: Default `True`. If `True` schema is generated without a `request` instance being passed to views. +* `patterns`: Default `None`. A list of URLs to inspect when generating the schema. If `None` project's URL conf will be used. +* `generator_class`: Default `rest_framework.schemas.SchemaGenerator`. May be used to specify a `SchemaGenerator` subclass to be passed to the `SchemaView`. +* `authentication_classes`: Default `api_settings.DEFAULT_AUTHENTICATION_CLASSES`. May be used to pass custom authentication classes to the `SchemaView`. +* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES`. May be used to pass custom permission classes to the `SchemaView`. +* `renderer_classes`: Default `None`. May be used to pass custom renderer classes to the `SchemaView`. If `None` the `SchemaView` will be configured with `DocumentationRenderer` and `CoreJSONRenderer` renderers, corresponding to the (default) `html` and `corejson` formats. + +#### `get_schemajs_view` + +* `title`: Default `None`. May be used to provide a descriptive title for the schema definition. +* `description`: Default `None`. May be used to provide a description for the schema definition. +* `schema_url`: Default `None`. May be used to pass a canonical base URL for the schema. +* `public`: Default `True`. If `True` schema is generated without a `request` instance being passed to views. +* `patterns`: Default `None`. A list of URLs to inspect when generating the schema. If `None` project's URL conf will be used. +* `generator_class`: Default `rest_framework.schemas.SchemaGenerator`. May be used to specify a `SchemaGenerator` subclass to be passed to the `SchemaView`. +* `authentication_classes`: Default `api_settings.DEFAULT_AUTHENTICATION_CLASSES`. May be used to pass custom authentication classes to the `SchemaView`. +* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES` May be used to pass custom permission classes to the `SchemaView`. + + +### Customising code samples + +The built-in API documentation includes automatically generated code samples for +each of the available API client libraries. + +You may customise these samples by subclassing `DocumentationRenderer`, setting +`languages` to the list of languages you wish to support: + + from rest_framework.renderers import DocumentationRenderer + + + class CustomRenderer(DocumentationRenderer): + languages = ['ruby', 'go'] + +For each language you need to provide an `intro` template, detailing installation instructions and such, +plus a generic template for making API requests, that can be filled with individual request details. +See the [templates for the bundled languages][client-library-templates] for examples. + +--- + +[client-library-templates]: https://github.com/encode/django-rest-framework/tree/master/rest_framework/templates/rest_framework/docs/langs \ No newline at end of file diff --git a/docs/coreapi/index.md b/docs/coreapi/index.md new file mode 100644 index 000000000..9195eb33e --- /dev/null +++ b/docs/coreapi/index.md @@ -0,0 +1,29 @@ +# Legacy CoreAPI Schemas Docs + +Use of CoreAPI-based schemas were deprecated with the introduction of native OpenAPI-based schema generation in Django REST Framework v3.10. + +See the [Version 3.10 Release Announcement](/community/3.10-announcement.md) for more details. + +---- + +You can continue to use CoreAPI schemas by setting the appropriate default schema class: + +```python +# In settings.py +REST_FRAMEWORK = { + 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', +} +``` + +Under-the-hood, any subclass of `coreapi.AutoSchema` here will trigger use of the old CoreAPI schemas. +**Otherwise** you will automatically be opted-in to the new OpenAPI schemas. + +All CoreAPI related code will be removed in Django REST Framework v3.12. Switch to OpenAPI schemas by then. + +---- + +For reference this folder contains the old CoreAPI related documentation: + +* [Tutorial 7: Schemas & client libraries](https://github.com/encode/django-rest-framework/blob/master/docs/coreapi//7-schemas-and-client-libraries.md). +* [Excerpts from _Documenting your API_ topic page](https://github.com/encode/django-rest-framework/blob/master/docs/coreapi//from-documenting-your-api.md). +* [`rest_framework.schemas` API Reference](https://github.com/encode/django-rest-framework/blob/master/docs/coreapi//schemas.md). diff --git a/docs/coreapi/schemas.md b/docs/coreapi/schemas.md new file mode 100644 index 000000000..b09b1606e --- /dev/null +++ b/docs/coreapi/schemas.md @@ -0,0 +1,838 @@ +source: schemas.py + +# Schemas + +> A machine-readable [schema] describes what resources are available via the API, what their URLs are, how they are represented and what operations they support. +> +> — Heroku, [JSON Schema for the Heroku Platform API][cite] + +API schemas are a useful tool that allow for a range of use cases, including +generating reference documentation, or driving dynamic client libraries that +can interact with your API. + +## Install Core API & PyYAML + +You'll need to install the `coreapi` package in order to add schema support +for REST framework. You probably also want to install `pyyaml`, so that you +can render the schema into the commonly used YAML-based OpenAPI format. + + pip install coreapi pyyaml + +## Quickstart + +There are two different ways you can serve a schema description for your API. + +### Generating a schema with the `generateschema` management command + +To generate a static API schema, use the `generateschema` management command. + +```shell +$ python manage.py generateschema > schema.yml +``` + +Once you've generated a schema in this way you can annotate it with any +additional information that cannot be automatically inferred by the schema +generator. + +You might want to check your API schema into version control and update it +with each new release, or serve the API schema from your site's static media. + +### Adding a view with `get_schema_view` + +To add a dynamically generated schema view to your API, use `get_schema_view`. + +```python +from rest_framework.schemas import get_schema_view + +schema_view = get_schema_view(title="Example API") + +urlpatterns = [ + url('^schema$', schema_view), + ... +] +``` + +See below [for more details](#the-get_schema_view-shortcut) on customizing a +dynamically generated schema view. + +## Internal schema representation + +REST framework uses [Core API][coreapi] in order to model schema information in +a format-independent representation. This information can then be rendered +into various different schema formats, or used to generate API documentation. + +When using Core API, a schema is represented as a `Document` which is the +top-level container object for information about the API. Available API +interactions are represented using `Link` objects. Each link includes a URL, +HTTP method, and may include a list of `Field` instances, which describe any +parameters that may be accepted by the API endpoint. The `Link` and `Field` +instances may also include descriptions, that allow an API schema to be +rendered into user documentation. + +Here's an example of an API description that includes a single `search` +endpoint: + + coreapi.Document( + title='Flight Search API', + url='https://api.example.org/', + content={ + 'search': coreapi.Link( + url='/search/', + action='get', + fields=[ + coreapi.Field( + name='from', + required=True, + location='query', + description='City name or airport code.' + ), + coreapi.Field( + name='to', + required=True, + location='query', + description='City name or airport code.' + ), + coreapi.Field( + name='date', + required=True, + location='query', + description='Flight date in "YYYY-MM-DD" format.' + ) + ], + description='Return flight availability and prices.' + ) + } + ) + +## Schema output formats + +In order to be presented in an HTTP response, the internal representation +has to be rendered into the actual bytes that are used in the response. + +REST framework includes a few different renderers that you can use for +encoding the API schema. + +* `renderers.OpenAPIRenderer` - Renders into YAML-based [OpenAPI][open-api], the most widely used API schema format. +* `renderers.JSONOpenAPIRenderer` - Renders into JSON-based [OpenAPI][open-api]. +* `renderers.CoreJSONRenderer` - Renders into [Core JSON][corejson], a format designed for +use with the `coreapi` client library. + + +[Core JSON][corejson] is designed as a canonical format for use with Core API. +REST framework includes a renderer class for handling this media type, which +is available as `renderers.CoreJSONRenderer`. + + +## Schemas vs Hypermedia + +It's worth pointing out here that Core API can also be used to model hypermedia +responses, which present an alternative interaction style to API schemas. + +With an API schema, the entire available interface is presented up-front +as a single endpoint. Responses to individual API endpoints are then typically +presented as plain data, without any further interactions contained in each +response. + +With Hypermedia, the client is instead presented with a document containing +both data and available interactions. Each interaction results in a new +document, detailing both the current state and the available interactions. + +Further information and support on building Hypermedia APIs with REST framework +is planned for a future version. + + +--- + +# Creating a schema + +REST framework includes functionality for auto-generating a schema, +or allows you to specify one explicitly. + +## Manual Schema Specification + +To manually specify a schema you create a Core API `Document`, similar to the +example above. + + schema = coreapi.Document( + title='Flight Search API', + content={ + ... + } + ) + + +## Automatic Schema Generation + +Automatic schema generation is provided by the `SchemaGenerator` class. + +`SchemaGenerator` processes a list of routed URL patterns and compiles the +appropriately structured Core API Document. + +Basic usage is just to provide the title for your schema and call +`get_schema()`: + + generator = schemas.SchemaGenerator(title='Flight Search API') + schema = generator.get_schema() + +## Per-View Schema Customisation + +By default, view introspection is performed by an `AutoSchema` instance +accessible via the `schema` attribute on `APIView`. This provides the +appropriate Core API `Link` object for the view, request method and path: + + auto_schema = view.schema + coreapi_link = auto_schema.get_link(...) + +(In compiling the schema, `SchemaGenerator` calls `view.schema.get_link()` for +each view, allowed method and path.) + +--- + +**Note**: For basic `APIView` subclasses, default introspection is essentially +limited to the URL kwarg path parameters. For `GenericAPIView` +subclasses, which includes all the provided class based views, `AutoSchema` will +attempt to introspect serialiser, pagination and filter fields, as well as +provide richer path field descriptions. (The key hooks here are the relevant +`GenericAPIView` attributes and methods: `get_serializer`, `pagination_class`, +`filter_backends` and so on.) + +--- + +To customise the `Link` generation you may: + +* Instantiate `AutoSchema` on your view with the `manual_fields` kwarg: + + from rest_framework.views import APIView + from rest_framework.schemas import AutoSchema + + class CustomView(APIView): + ... + schema = AutoSchema( + manual_fields=[ + coreapi.Field("extra_field", ...), + ] + ) + + This allows extension for the most common case without subclassing. + +* Provide an `AutoSchema` subclass with more complex customisation: + + from rest_framework.views import APIView + from rest_framework.schemas import AutoSchema + + class CustomSchema(AutoSchema): + def get_link(...): + # Implement custom introspection here (or in other sub-methods) + + class CustomView(APIView): + ... + schema = CustomSchema() + + This provides complete control over view introspection. + +* Instantiate `ManualSchema` on your view, providing the Core API `Fields` for + the view explicitly: + + from rest_framework.views import APIView + from rest_framework.schemas import ManualSchema + + class CustomView(APIView): + ... + schema = ManualSchema(fields=[ + coreapi.Field( + "first_field", + required=True, + location="path", + schema=coreschema.String() + ), + coreapi.Field( + "second_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + + This allows manually specifying the schema for some views whilst maintaining + automatic generation elsewhere. + +You may disable schema generation for a view by setting `schema` to `None`: + + class CustomView(APIView): + ... + schema = None # Will not appear in schema + +This also applies to extra actions for `ViewSet`s: + + class CustomViewSet(viewsets.ModelViewSet): + + @action(detail=True, schema=None) + def extra_action(self, request, pk=None): + ... + +--- + +**Note**: For full details on `SchemaGenerator` plus the `AutoSchema` and +`ManualSchema` descriptors see the [API Reference below](#api-reference). + +--- + +# Adding a schema view + +There are a few different ways to add a schema view to your API, depending on +exactly what you need. + +## The get_schema_view shortcut + +The simplest way to include a schema in your project is to use the +`get_schema_view()` function. + + from rest_framework.schemas import get_schema_view + + schema_view = get_schema_view(title="Server Monitoring API") + + 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/coreapi+json + HTTP/1.0 200 OK + Allow: GET, HEAD, OPTIONS + Content-Type: application/vnd.coreapi+json + + { + "_meta": { + "title": "Server Monitoring API" + }, + "_type": "document", + ... + } + +The arguments to `get_schema_view()` are: + +#### `title` + +May be used to provide a descriptive title for the schema definition. + +#### `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/' + ) + +#### `urlconf` + +A string representing the import path to the URL conf that you want +to generate an API schema for. This defaults to the value of Django's +ROOT_URLCONF setting. + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + urlconf='myproject.urls' + ) + +#### `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.schemas import get_schema_view + from rest_framework.renderers import JSONOpenAPIRenderer + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + renderer_classes=[JSONOpenAPIRenderer] + ) + +#### `patterns` + +List of url patterns to limit the schema introspection to. If you only want the `myproject.api` urls +to be exposed in the schema: + + schema_url_patterns = [ + url(r'^api/', include('myproject.api.urls')), + ] + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + patterns=schema_url_patterns, + ) + +#### `generator_class` + +May be used to specify a `SchemaGenerator` subclass to be passed to the +`SchemaView`. + +#### `authentication_classes` + +May be used to specify the list of authentication classes that will apply to the schema endpoint. +Defaults to `settings.DEFAULT_AUTHENTICATION_CLASSES` + +#### `permission_classes` + +May be used to specify the list of permission classes that will apply to the schema endpoint. +Defaults to `settings.DEFAULT_PERMISSION_CLASSES` + +## Using an explicit schema 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 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. + +**views.py:** + + 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.OpenAPIRenderer]) + def schema_view(request): + schema = generator.get_schema(request) + return response.Response(schema) + +**urls.py:** + + urlpatterns = [ + url('/', schema_view), + ... + ] + +You can also serve different schemas to different users, depending on the +permissions they have available. This approach can be used to ensure that +unauthenticated requests are presented with a different schema to +authenticated requests, or to ensure that different parts of the API are +made visible to different users depending on their role. + +In order to present a schema with endpoints filtered by user permissions, +you need to pass the `request` argument to the `get_schema()` method, like so: + + @api_view() + @renderer_classes([renderers.OpenAPIRenderer]) + def schema_view(request): + generator = schemas.SchemaGenerator(title='Bookings API') + return response.Response(generator.get_schema(request=request)) + +## Explicit schema definition + +An alternative to the auto-generated approach is to specify the API schema +explicitly, by declaring a `Document` object in your codebase. Doing so is a +little more work, but ensures that you have full control over the schema +representation. + + import coreapi + from rest_framework.decorators import api_view, renderer_classes + from rest_framework import renderers, response + + schema = coreapi.Document( + title='Bookings API', + content={ + ... + } + ) + + @api_view() + @renderer_classes([renderers.OpenAPIRenderer]) + def schema_view(request): + return response.Response(schema) + +--- + +# Schemas as documentation + +One common usage of API schemas is to use them to build documentation pages. + +The schema generation in REST framework uses docstrings to automatically +populate descriptions in the schema document. + +These descriptions will be based on: + +* The corresponding method docstring if one exists. +* A named section within the class docstring, which can be either single line or multi-line. +* The class docstring. + +## Examples + +An `APIView`, with an explicit method docstring. + + class ListUsernames(APIView): + def get(self, request): + """ + Return a list of all user names in the system. + """ + usernames = [user.username for user in User.objects.all()] + return Response(usernames) + +A `ViewSet`, with an explict action docstring. + + class ListUsernames(ViewSet): + def list(self, request): + """ + Return a list of all user names in the system. + """ + usernames = [user.username for user in User.objects.all()] + return Response(usernames) + +A generic view with sections in the class docstring, using single-line style. + + class UserList(generics.ListCreateAPIView): + """ + get: List all the users. + post: Create a new user. + """ + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = (IsAdminUser,) + +A generic viewset with sections in the class docstring, using multi-line style. + + class UserViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + + retrieve: + Return a user instance. + + list: + Return all users, ordered by most recently joined. + """ + queryset = User.objects.all().order_by('-date_joined') + serializer_class = UserSerializer + +--- + +# API Reference + +## SchemaGenerator + +A class that walks a list of routed URL patterns, requests the schema for each view, +and collates the resulting CoreAPI Document. + +Typically you'll instantiate `SchemaGenerator` with a single argument, like so: + + generator = SchemaGenerator(title='Stock Prices API') + +Arguments: + +* `title` **required** - The name of the API. +* `url` - The root URL of the API schema. This option is not required unless the schema is included under path prefix. +* `patterns` - A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. +* `urlconf` - A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. + +### get_schema(self, request) + +Returns a `coreapi.Document` instance that represents the API schema. + + @api_view + @renderer_classes([renderers.OpenAPIRenderer]) + def schema_view(request): + generator = schemas.SchemaGenerator(title='Bookings API') + return Response(generator.get_schema()) + +The `request` argument is optional, and may be used if you want to apply per-user +permissions to the resulting schema generation. + +### get_links(self, request) + +Return a nested dictionary containing all the links that should be included in the API schema. + +This is a good point to override if you want to modify the resulting structure of the generated schema, +as you can build a new dictionary with a different layout. + + +## AutoSchema + +A class that deals with introspection of individual views for schema generation. + +`AutoSchema` is attached to `APIView` via the `schema` attribute. + +The `AutoSchema` constructor takes a single keyword argument `manual_fields`. + +**`manual_fields`**: a `list` of `coreapi.Field` instances that will be added to +the generated fields. Generated fields with a matching `name` will be overwritten. + + class CustomView(APIView): + schema = AutoSchema(manual_fields=[ + coreapi.Field( + "my_extra_field", + required=True, + location="path", + schema=coreschema.String() + ), + ]) + +For more advanced customisation subclass `AutoSchema` to customise schema generation. + + class CustomViewSchema(AutoSchema): + """ + Overrides `get_link()` to provide Custom Behavior X + """ + + def get_link(self, path, method, base_url): + link = super().get_link(path, method, base_url) + # Do something to customize link here... + return link + + class MyView(APIView): + schema = CustomViewSchema() + +The following methods are available to override. + +### get_link(self, path, method, base_url) + +Returns a `coreapi.Link` instance corresponding to the given view. + +This is the main entry point. +You can override this if you need to provide custom behaviors for particular views. + +### get_description(self, path, method) + +Returns a string to use as the link description. By default this is based on the +view docstring as described in the "Schemas as Documentation" section above. + +### get_encoding(self, path, method) + +Returns a string to indicate the encoding for any request body, when interacting +with the given view. Eg. `'application/json'`. May return a blank string for views +that do not expect a request body. + +### get_path_fields(self, path, method): + +Return a list of `coreapi.Field()` instances. One for each path parameter in the URL. + +### get_serializer_fields(self, path, method) + +Return a list of `coreapi.Field()` instances. One for each field in the serializer class used by the view. + +### get_pagination_fields(self, path, method) + +Return a list of `coreapi.Field()` instances, as returned by the `get_schema_fields()` method on any pagination class used by the view. + +### get_filter_fields(self, path, method) + +Return a list of `coreapi.Field()` instances, as returned by the `get_schema_fields()` method of any filter classes used by the view. + +### get_manual_fields(self, path, method) + +Return a list of `coreapi.Field()` instances to be added to or replace generated fields. Defaults to (optional) `manual_fields` passed to `AutoSchema` constructor. + +May be overridden to customise manual fields by `path` or `method`. For example, a per-method adjustment may look like this: + +```python +def get_manual_fields(self, path, method): + """Example adding per-method fields.""" + + extra_fields = [] + if method=='GET': + extra_fields = # ... list of extra fields for GET ... + if method=='POST': + extra_fields = # ... list of extra fields for POST ... + + manual_fields = super().get_manual_fields(path, method) + return manual_fields + extra_fields +``` + +### update_fields(fields, update_with) + +Utility `staticmethod`. Encapsulates logic to add or replace fields from a list +by `Field.name`. May be overridden to adjust replacement criteria. + + +## ManualSchema + +Allows manually providing a list of `coreapi.Field` instances for the schema, +plus an optional description. + + class MyView(APIView): + schema = ManualSchema(fields=[ + coreapi.Field( + "first_field", + required=True, + location="path", + schema=coreschema.String() + ), + coreapi.Field( + "second_field", + required=True, + location="path", + schema=coreschema.String() + ), + ] + ) + +The `ManualSchema` constructor takes two arguments: + +**`fields`**: A list of `coreapi.Field` instances. Required. + +**`description`**: A string description. Optional. + +**`encoding`**: Default `None`. A string encoding, e.g `application/json`. Optional. + +--- + +## Core API + +This documentation gives a brief overview of the components within the `coreapi` +package that are used to represent an API schema. + +Note that these classes are imported from the `coreapi` package, rather than +from the `rest_framework` package. + +### Document + +Represents a container for the API schema. + +#### `title` + +A name for the API. + +#### `url` + +A canonical URL for the API. + +#### `content` + +A dictionary, containing the `Link` objects that the schema contains. + +In order to provide more structure to the schema, the `content` dictionary +may be nested, typically to a second level. For example: + + content={ + "bookings": { + "list": Link(...), + "create": Link(...), + ... + }, + "venues": { + "list": Link(...), + ... + }, + ... + } + +### Link + +Represents an individual API endpoint. + +#### `url` + +The URL of the endpoint. May be a URI template, such as `/users/{username}/`. + +#### `action` + +The HTTP method associated with the endpoint. Note that URLs that support +more than one HTTP method, should correspond to a single `Link` for each. + +#### `fields` + +A list of `Field` instances, describing the available parameters on the input. + +#### `description` + +A short description of the meaning and intended usage of the endpoint. + +### Field + +Represents a single input parameter on a given API endpoint. + +#### `name` + +A descriptive name for the input. + +#### `required` + +A boolean, indicated if the client is required to included a value, or if +the parameter can be omitted. + +#### `location` + +Determines how the information is encoded into the request. Should be one of +the following strings: + +**"path"** + +Included in a templated URI. For example a `url` value of `/products/{product_code}/` could be used together with a `"path"` field, to handle API inputs in a URL path such as `/products/slim-fit-jeans/`. + +These fields will normally correspond with [named arguments in the project URL conf][named-arguments]. + +**"query"** + +Included as a URL query parameter. For example `?search=sale`. Typically for `GET` requests. + +These fields will normally correspond with pagination and filtering controls on a view. + +**"form"** + +Included in the request body, as a single item of a JSON object or HTML form. For example `{"colour": "blue", ...}`. Typically for `POST`, `PUT` and `PATCH` requests. Multiple `"form"` fields may be included on a single link. + +These fields will normally correspond with serializer fields on a view. + +**"body"** + +Included as the complete request body. Typically for `POST`, `PUT` and `PATCH` requests. No more than one `"body"` field may exist on a link. May not be used together with `"form"` fields. + +These fields will normally correspond with views that use `ListSerializer` to validate the request input, or with file upload views. + +#### `encoding` + +**"application/json"** + +JSON encoded request content. Corresponds to views using `JSONParser`. +Valid only if either one or more `location="form"` fields, or a single +`location="body"` field is included on the `Link`. + +**"multipart/form-data"** + +Multipart encoded request content. Corresponds to views using `MultiPartParser`. +Valid only if one or more `location="form"` fields is included on the `Link`. + +**"application/x-www-form-urlencoded"** + +URL encoded request content. Corresponds to views using `FormParser`. Valid +only if one or more `location="form"` fields is included on the `Link`. + +**"application/octet-stream"** + +Binary upload request content. Corresponds to views using `FileUploadParser`. +Valid only if a `location="body"` field is included on the `Link`. + +#### `description` + +A short description of the meaning and intended usage of the input field. + + +--- + +# Third party packages + +## drf-yasg - Yet Another Swagger Generator + +[drf-yasg][drf-yasg] generates [OpenAPI][open-api] documents suitable for code generation - nested schemas, +named models, response bodies, enum/pattern/min/max validators, form parameters, etc. + +[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api +[coreapi]: https://www.coreapi.org/ +[corejson]: https://www.coreapi.org/specification/encoding/#core-json-encoding +[drf-yasg]: https://github.com/axnsan12/drf-yasg/ +[open-api]: https://openapis.org/ +[json-hyperschema]: https://json-schema.org/latest/json-schema-hypermedia.html +[api-blueprint]: https://apiblueprint.org/ +[static-files]: https://docs.djangoproject.com/en/stable/howto/static-files/ +[named-arguments]: https://docs.djangoproject.com/en/stable/topics/http/urls/#named-groups diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md index e5bf57530..f663e22e4 100644 --- a/docs/topics/documenting-your-api.md +++ b/docs/topics/documenting-your-api.md @@ -4,176 +4,121 @@ > > — Roy Fielding, [REST APIs must be hypertext driven][cite] -REST framework provides built-in support for API documentation. There are also a number of great third-party documentation tools available. +REST framework provides built-in support for generating OpenAPI schemas, which +can be used with tools that allow you to build API documentation. -## Built-in API documentation +There are also a number of great third-party documentation packages available. -The built-in API documentation includes: +## Generating documentation from OpenAPI schemas -* Documentation of API endpoints. -* Automatically generated code samples for each of the available API client libraries. -* Support for API interaction. +There are a number of packages available that allow you to generate HTML +documenation pages from OpenAPI schemas. -### Installation +Two popular options are [Swagger UI][swagger-ui] and [ReDoc][redoc]. -The `coreapi` library is required as a dependency for the API docs. Make sure -to install the latest version. The `Pygments` and `Markdown` libraries -are optional but recommended. +Both require little more than the location of your static schema file or +dynamic `SchemaView` endpoint. -To install the API documentation, you'll need to include it in your project's URLconf: +### A minimal example with Swagger UI - from rest_framework.documentation import include_docs_urls +Assuming you've followed the example from the schemas documentation for routing +a dynamic `SchemaView`, a minimal Django template for using Swagger UI might be +this: - urlpatterns = [ - ... - url(r'^docs/', include_docs_urls(title='My API title')) - ] +```html + + +
+