mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-07 13:54:47 +03:00
Added schema descriptions
This commit is contained in:
parent
cd826ce422
commit
7e3a3a4081
|
@ -242,6 +242,14 @@ You could then either:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Alternate schema formats
|
# Alternate schema formats
|
||||||
|
|
||||||
In order to support an alternate schema format, you need to implement a custom renderer
|
In order to support an alternate schema format, you need to implement a custom renderer
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
|
@ -15,6 +16,8 @@ from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:')
|
||||||
|
|
||||||
|
|
||||||
def as_query_fields(items):
|
def as_query_fields(items):
|
||||||
"""
|
"""
|
||||||
|
@ -53,8 +56,7 @@ def insert_into(target, keys, value):
|
||||||
|
|
||||||
def is_custom_action(action):
|
def is_custom_action(action):
|
||||||
return action not in set([
|
return action not in set([
|
||||||
'read', 'retrieve', 'list',
|
'retrieve', 'list', 'create', 'update', 'partial_update', 'destroy'
|
||||||
'create', 'update', 'partial_update', 'delete', 'destroy'
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,17 +173,12 @@ class EndpointInspector(object):
|
||||||
class SchemaGenerator(object):
|
class SchemaGenerator(object):
|
||||||
# Map methods onto 'actions' that are the names used in the link layout.
|
# Map methods onto 'actions' that are the names used in the link layout.
|
||||||
default_mapping = {
|
default_mapping = {
|
||||||
'get': 'read',
|
'get': 'retrieve',
|
||||||
'post': 'create',
|
'post': 'create',
|
||||||
'put': 'update',
|
'put': 'update',
|
||||||
'patch': 'partial_update',
|
'patch': 'partial_update',
|
||||||
'delete': 'destroy',
|
'delete': 'destroy',
|
||||||
}
|
}
|
||||||
# Coerce the following viewset actions into different names.
|
|
||||||
coerce_actions = {
|
|
||||||
'retrieve': 'read',
|
|
||||||
'destroy': 'delete'
|
|
||||||
}
|
|
||||||
endpoint_inspector_cls = EndpointInspector
|
endpoint_inspector_cls = EndpointInspector
|
||||||
|
|
||||||
def __init__(self, title=None, url=None, patterns=None, urlconf=None):
|
def __init__(self, title=None, url=None, patterns=None, urlconf=None):
|
||||||
|
@ -283,6 +280,8 @@ class SchemaGenerator(object):
|
||||||
else:
|
else:
|
||||||
encoding = None
|
encoding = None
|
||||||
|
|
||||||
|
description = self.get_description(path, method, view)
|
||||||
|
|
||||||
if self.url and path.startswith('/'):
|
if self.url and path.startswith('/'):
|
||||||
path = path[1:]
|
path = path[1:]
|
||||||
|
|
||||||
|
@ -290,9 +289,38 @@ class SchemaGenerator(object):
|
||||||
url=urlparse.urljoin(self.url, path),
|
url=urlparse.urljoin(self.url, path),
|
||||||
action=method.lower(),
|
action=method.lower(),
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
fields=fields
|
fields=fields,
|
||||||
|
description=description
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_description(self, path, method, view):
|
||||||
|
"""
|
||||||
|
Determine a link description.
|
||||||
|
|
||||||
|
This with either be the class docstring, or a specific section for it.
|
||||||
|
|
||||||
|
For views, use method names, eg `get:` to introduce a section.
|
||||||
|
For viewsets, use action names, eg `retrieve:` to introduce a section.
|
||||||
|
|
||||||
|
The section names will correspond to the methods on the class.
|
||||||
|
"""
|
||||||
|
view_description = view.get_view_description()
|
||||||
|
lines = [line.strip() for line in view_description.splitlines()]
|
||||||
|
current_section = ''
|
||||||
|
sections = {'': ''}
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if header_regex.match(line):
|
||||||
|
current_section, seperator, lead = line.partition(':')
|
||||||
|
sections[current_section] = lead.strip()
|
||||||
|
else:
|
||||||
|
sections[current_section] += line + '\n'
|
||||||
|
|
||||||
|
header = getattr(view, 'action', method.lower())
|
||||||
|
if header in sections:
|
||||||
|
return sections[header].strip()
|
||||||
|
return sections[''].strip()
|
||||||
|
|
||||||
def get_encoding(self, path, method, view):
|
def get_encoding(self, path, method, view):
|
||||||
"""
|
"""
|
||||||
Return the 'encoding' parameter to use for a given endpoint.
|
Return the 'encoding' parameter to use for a given endpoint.
|
||||||
|
@ -401,10 +429,7 @@ class SchemaGenerator(object):
|
||||||
"""
|
"""
|
||||||
if hasattr(view, 'action'):
|
if hasattr(view, 'action'):
|
||||||
# Viewsets have explicitly named actions.
|
# Viewsets have explicitly named actions.
|
||||||
if view.action in self.coerce_actions:
|
action = view.action
|
||||||
action = self.coerce_actions[view.action]
|
|
||||||
else:
|
|
||||||
action = view.action
|
|
||||||
else:
|
else:
|
||||||
# Views have no associated action, so we determine one from the method.
|
# Views have no associated action, so we determine one from the method.
|
||||||
if is_list_view(path, method, view):
|
if is_list_view(path, method, view):
|
||||||
|
|
|
@ -94,12 +94,12 @@ class TestRouterGeneratedSchema(TestCase):
|
||||||
action='get'
|
action='get'
|
||||||
),
|
),
|
||||||
'custom_list_action_multiple_methods': {
|
'custom_list_action_multiple_methods': {
|
||||||
'read': coreapi.Link(
|
'retrieve': coreapi.Link(
|
||||||
url='/example/custom_list_action_multiple_methods/',
|
url='/example/custom_list_action_multiple_methods/',
|
||||||
action='get'
|
action='get'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
'read': coreapi.Link(
|
'retrieve': coreapi.Link(
|
||||||
url='/example/{pk}/',
|
url='/example/{pk}/',
|
||||||
action='get',
|
action='get',
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -138,7 +138,7 @@ class TestRouterGeneratedSchema(TestCase):
|
||||||
coreapi.Field('b', required=False, location='form')
|
coreapi.Field('b', required=False, location='form')
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
'read': coreapi.Link(
|
'retrieve': coreapi.Link(
|
||||||
url='/example/{pk}/',
|
url='/example/{pk}/',
|
||||||
action='get',
|
action='get',
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -160,7 +160,7 @@ class TestRouterGeneratedSchema(TestCase):
|
||||||
action='get'
|
action='get'
|
||||||
),
|
),
|
||||||
'custom_list_action_multiple_methods': {
|
'custom_list_action_multiple_methods': {
|
||||||
'read': coreapi.Link(
|
'retrieve': coreapi.Link(
|
||||||
url='/example/custom_list_action_multiple_methods/',
|
url='/example/custom_list_action_multiple_methods/',
|
||||||
action='get'
|
action='get'
|
||||||
),
|
),
|
||||||
|
@ -189,7 +189,7 @@ class TestRouterGeneratedSchema(TestCase):
|
||||||
coreapi.Field('b', required=False, location='form')
|
coreapi.Field('b', required=False, location='form')
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
'delete': coreapi.Link(
|
'destroy': coreapi.Link(
|
||||||
url='/example/{pk}/',
|
url='/example/{pk}/',
|
||||||
action='delete',
|
action='delete',
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -249,7 +249,7 @@ class TestSchemaGenerator(TestCase):
|
||||||
action='get',
|
action='get',
|
||||||
fields=[]
|
fields=[]
|
||||||
),
|
),
|
||||||
'read': coreapi.Link(
|
'retrieve': coreapi.Link(
|
||||||
url='/example/{pk}/',
|
url='/example/{pk}/',
|
||||||
action='get',
|
action='get',
|
||||||
fields=[
|
fields=[
|
||||||
|
|
Loading…
Reference in New Issue
Block a user