mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-05 13:00:12 +03:00
Merge remote-tracking branch 'tomchristie/docs' into docs
This commit is contained in:
commit
05066b60da
|
@ -180,6 +180,13 @@ except (ImportError, SyntaxError):
|
||||||
uritemplate = None
|
uritemplate = None
|
||||||
|
|
||||||
|
|
||||||
|
# coreschema is optional
|
||||||
|
try:
|
||||||
|
import coreschema
|
||||||
|
except ImportError:
|
||||||
|
coreschema = None
|
||||||
|
|
||||||
|
|
||||||
# django-filter is optional
|
# django-filter is optional
|
||||||
try:
|
try:
|
||||||
import django_filters
|
import django_filters
|
||||||
|
|
|
@ -16,7 +16,7 @@ from django.utils import six
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
coreapi, distinct, django_filters, guardian, template_render
|
coreapi, coreschema, distinct, django_filters, guardian, template_render
|
||||||
)
|
)
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ class BaseFilterBackend(object):
|
||||||
|
|
||||||
def get_schema_fields(self, view):
|
def get_schema_fields(self, view):
|
||||||
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
||||||
|
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,7 +163,18 @@ class SearchFilter(BaseFilterBackend):
|
||||||
|
|
||||||
def get_schema_fields(self, view):
|
def get_schema_fields(self, view):
|
||||||
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
||||||
return [coreapi.Field(name=self.search_param, required=False, location='query')]
|
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
|
||||||
|
return [
|
||||||
|
coreapi.Field(
|
||||||
|
name=self.search_param,
|
||||||
|
required=False,
|
||||||
|
location='query',
|
||||||
|
schema=coreschema.String(
|
||||||
|
title='Search',
|
||||||
|
description='...'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class OrderingFilter(BaseFilterBackend):
|
class OrderingFilter(BaseFilterBackend):
|
||||||
|
@ -280,7 +292,18 @@ class OrderingFilter(BaseFilterBackend):
|
||||||
|
|
||||||
def get_schema_fields(self, view):
|
def get_schema_fields(self, view):
|
||||||
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
||||||
return [coreapi.Field(name=self.ordering_param, required=False, location='query')]
|
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
|
||||||
|
return [
|
||||||
|
coreapi.Field(
|
||||||
|
name=self.ordering_param,
|
||||||
|
required=False,
|
||||||
|
location='query',
|
||||||
|
schema=coreschema.String(
|
||||||
|
title='Ordering',
|
||||||
|
description='...'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DjangoObjectPermissionsFilter(BaseFilterBackend):
|
class DjangoObjectPermissionsFilter(BaseFilterBackend):
|
||||||
|
|
|
@ -16,7 +16,7 @@ from django.utils.encoding import force_text
|
||||||
from django.utils.six.moves.urllib import parse as urlparse
|
from django.utils.six.moves.urllib import parse as urlparse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.compat import coreapi, template_render
|
from rest_framework.compat import coreapi, coreschema, template_render
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
|
@ -289,12 +289,16 @@ class PageNumberPagination(BasePagination):
|
||||||
|
|
||||||
def get_schema_fields(self, view):
|
def get_schema_fields(self, view):
|
||||||
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
||||||
|
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
|
||||||
fields = [
|
fields = [
|
||||||
coreapi.Field(
|
coreapi.Field(
|
||||||
name=self.page_query_param,
|
name=self.page_query_param,
|
||||||
required=False,
|
required=False,
|
||||||
location='query',
|
location='query',
|
||||||
#description=force_text(self.page_query_description)
|
schema=coreschema.Integer(
|
||||||
|
title='Page',
|
||||||
|
description=force_text(self.page_query_description)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
if self.page_size_query_param is not None:
|
if self.page_size_query_param is not None:
|
||||||
|
@ -439,18 +443,25 @@ class LimitOffsetPagination(BasePagination):
|
||||||
|
|
||||||
def get_schema_fields(self, view):
|
def get_schema_fields(self, view):
|
||||||
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
||||||
|
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
|
||||||
return [
|
return [
|
||||||
coreapi.Field(
|
coreapi.Field(
|
||||||
name=self.limit_query_param,
|
name=self.limit_query_param,
|
||||||
required=False,
|
required=False,
|
||||||
location='query',
|
location='query',
|
||||||
#description=force_text(self.limit_query_description)
|
schema=coreschema.Integer(
|
||||||
|
title='Limit',
|
||||||
|
description=force_text(self.limit_query_description)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
coreapi.Field(
|
coreapi.Field(
|
||||||
name=self.offset_query_param,
|
name=self.offset_query_param,
|
||||||
required=False,
|
required=False,
|
||||||
location='query',
|
location='query',
|
||||||
#description=force_text(self.offset_query_description)
|
schema=coreschema.Integer(
|
||||||
|
title='Offset',
|
||||||
|
description=force_text(self.offset_query_description)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -764,11 +775,15 @@ class CursorPagination(BasePagination):
|
||||||
|
|
||||||
def get_schema_fields(self, view):
|
def get_schema_fields(self, view):
|
||||||
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
|
||||||
|
assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
|
||||||
return [
|
return [
|
||||||
coreapi.Field(
|
coreapi.Field(
|
||||||
name=self.cursor_query_param,
|
name=self.cursor_query_param,
|
||||||
required=False,
|
required=False,
|
||||||
location='query',
|
location='query',
|
||||||
description=force_text(self.cursor_query_description)
|
schema=coreschema.String(
|
||||||
|
title='Cursor',
|
||||||
|
description=force_text(self.cursor_query_description)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -808,7 +808,7 @@ class DocumentationRenderer(BaseRenderer):
|
||||||
code_style = formatter.get_style_defs('.highlight')
|
code_style = formatter.get_style_defs('.highlight')
|
||||||
langs = ['shell', 'javascript', 'python']
|
langs = ['shell', 'javascript', 'python']
|
||||||
codec = coreapi.codecs.CoreJSONCodec()
|
codec = coreapi.codecs.CoreJSONCodec()
|
||||||
schema = mark_safe(codec.encode(data))
|
schema = mark_safe(json.dumps(codec.encode(data)))
|
||||||
return {
|
return {
|
||||||
'document': data,
|
'document': data,
|
||||||
'langs': langs,
|
'langs': langs,
|
||||||
|
|
|
@ -13,7 +13,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import exceptions, renderers, serializers
|
from rest_framework import exceptions, renderers, serializers
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
RegexURLPattern, RegexURLResolver, coreapi, uritemplate, urlparse
|
RegexURLPattern, RegexURLResolver, coreapi, coreschema, uritemplate, urlparse
|
||||||
)
|
)
|
||||||
from rest_framework.request import clone_request
|
from rest_framework.request import clone_request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -26,36 +26,62 @@ from rest_framework.views import APIView
|
||||||
|
|
||||||
header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:')
|
header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:')
|
||||||
|
|
||||||
types_lookup = ClassLookupDict({
|
|
||||||
serializers.Field: 'string',
|
|
||||||
serializers.IntegerField: 'integer',
|
|
||||||
serializers.FloatField: 'number',
|
|
||||||
serializers.DecimalField: 'number',
|
|
||||||
serializers.BooleanField: 'boolean',
|
|
||||||
serializers.FileField: 'file',
|
|
||||||
serializers.MultipleChoiceField: 'array',
|
|
||||||
serializers.ManyRelatedField: 'array',
|
|
||||||
serializers.Serializer: 'object',
|
|
||||||
serializers.ListSerializer: 'array'
|
|
||||||
})
|
|
||||||
|
|
||||||
input_lookup = ClassLookupDict({
|
def field_to_schema(field):
|
||||||
serializers.Field: 'text',
|
title = force_text(field.label) if field.label else ''
|
||||||
serializers.IntegerField: 'number',
|
description = force_text(field.help_text) if field.help_text else ''
|
||||||
serializers.FloatField: 'number',
|
|
||||||
serializers.DecimalField: 'number',
|
|
||||||
serializers.BooleanField: 'checkbox',
|
|
||||||
serializers.FileField: 'file',
|
|
||||||
serializers.ChoiceField: 'select'
|
|
||||||
})
|
|
||||||
|
|
||||||
|
if isinstance(field, serializers.ListSerializer):
|
||||||
|
child_schema = serializer_to_schema(field.child)
|
||||||
|
return coreschema.Array(
|
||||||
|
items=child_schema,
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
)
|
||||||
|
elif isinstance(field, serializers.Serializer):
|
||||||
|
return coreschema.Object(
|
||||||
|
properties={
|
||||||
|
key: serializer_to_schema(value)
|
||||||
|
for key, value
|
||||||
|
in field.fields.items()
|
||||||
|
},
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
)
|
||||||
|
elif isinstance(field, serializers.ManyRelatedField):
|
||||||
|
return coreschema.Array(
|
||||||
|
items=coreschema.String(),
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
)
|
||||||
|
elif isinstance(field, serializers.RelatedField):
|
||||||
|
return coreschema.String(title=title, description=description)
|
||||||
|
elif isinstance(field, serializers.MultipleChoiceField):
|
||||||
|
return coreschema.Array(
|
||||||
|
items=coreschema.Enum(enum=list(field.choices.values())),
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
)
|
||||||
|
elif isinstance(field, serializers.ChoiceField):
|
||||||
|
return coreschema.Enum(
|
||||||
|
enum=list(field.choices.values()),
|
||||||
|
title=title,
|
||||||
|
description=description
|
||||||
|
)
|
||||||
|
elif isinstance(field, serializers.BooleanField):
|
||||||
|
return coreschema.Boolean(title=title, description=description)
|
||||||
|
elif isinstance(field, (serializers.DecimalField, serializers.FloatField)):
|
||||||
|
return coreschema.Number(title=title, description=description)
|
||||||
|
elif isinstance(field, serializers.IntegerField):
|
||||||
|
return coreschema.Integer(title=title, description=description)
|
||||||
|
|
||||||
def determine_input(field):
|
if field.style.get('base_template') == 'textarea.html':
|
||||||
input_type = input_lookup[field]
|
return coreschema.String(
|
||||||
base_template = field.style.get('base_template')
|
title=title,
|
||||||
if base_template == 'textarea.html':
|
description=description,
|
||||||
input_type = 'textarea'
|
format='textarea'
|
||||||
return input_type
|
)
|
||||||
|
return coreschema.String(title=title, description=description)
|
||||||
|
|
||||||
|
|
||||||
def common_path(paths):
|
def common_path(paths):
|
||||||
|
@ -252,6 +278,7 @@ class SchemaGenerator(object):
|
||||||
|
|
||||||
def __init__(self, title=None, url=None, patterns=None, urlconf=None):
|
def __init__(self, title=None, url=None, patterns=None, urlconf=None):
|
||||||
assert coreapi, '`coreapi` must be installed for schema support.'
|
assert coreapi, '`coreapi` must be installed for schema support.'
|
||||||
|
assert coreschema, '`coreschema` must be installed for schema support.'
|
||||||
|
|
||||||
if url and not url.endswith('/'):
|
if url and not url.endswith('/'):
|
||||||
url += '/'
|
url += '/'
|
||||||
|
@ -493,8 +520,9 @@ class SchemaGenerator(object):
|
||||||
fields = []
|
fields = []
|
||||||
|
|
||||||
for variable in uritemplate.variables(path):
|
for variable in uritemplate.variables(path):
|
||||||
title = None
|
title = ''
|
||||||
description = None
|
description = ''
|
||||||
|
schema_cls = coreschema.String
|
||||||
if model is not None:
|
if model is not None:
|
||||||
# Attempt to infer a field description if possible.
|
# Attempt to infer a field description if possible.
|
||||||
try:
|
try:
|
||||||
|
@ -510,12 +538,14 @@ class SchemaGenerator(object):
|
||||||
elif model_field is not None and model_field.primary_key:
|
elif model_field is not None and model_field.primary_key:
|
||||||
description = get_pk_description(model, model_field)
|
description = get_pk_description(model, model_field)
|
||||||
|
|
||||||
|
if isinstance(model_field, models.AutoField):
|
||||||
|
schema_cls = coreschema.Integer
|
||||||
|
|
||||||
field = coreapi.Field(
|
field = coreapi.Field(
|
||||||
name=variable,
|
name=variable,
|
||||||
location='path',
|
location='path',
|
||||||
required=True,
|
required=True,
|
||||||
#title='' if (title is None) else title,
|
schema=schema_cls(title=title, description=description)
|
||||||
#description='' if (description is None) else description
|
|
||||||
)
|
)
|
||||||
fields.append(field)
|
fields.append(field)
|
||||||
|
|
||||||
|
@ -540,7 +570,7 @@ class SchemaGenerator(object):
|
||||||
name='data',
|
name='data',
|
||||||
location='body',
|
location='body',
|
||||||
required=True,
|
required=True,
|
||||||
#type='array'
|
schema=coreschema.Array()
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -553,17 +583,11 @@ class SchemaGenerator(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
required = field.required and method != 'PATCH'
|
required = field.required and method != 'PATCH'
|
||||||
title = force_text(field.label) if field.label else ''
|
|
||||||
description = force_text(field.help_text) if field.help_text else ''
|
|
||||||
field = coreapi.Field(
|
field = coreapi.Field(
|
||||||
name=field.field_name,
|
name=field.field_name,
|
||||||
location='form',
|
location='form',
|
||||||
required=required,
|
required=required,
|
||||||
#title=title,
|
schema=field_to_schema(field)
|
||||||
#description=description,
|
|
||||||
#type=types_lookup[field],
|
|
||||||
#input=determine_input(field),
|
|
||||||
#choices=getattr(field, 'choices', None)
|
|
||||||
)
|
)
|
||||||
fields.append(field)
|
fields.append(field)
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
const coreapi = window.coreapi
|
const coreapi = window.coreapi
|
||||||
const codec = new coreapi.codecs.CoreJSONCodec()
|
const codec = new coreapi.codecs.CoreJSONCodec()
|
||||||
const schema = {{ schema }}
|
const schema = {{ schema }}
|
||||||
const doc = codec.decode(schema, {preloaded: true})
|
const doc = codec.decode(schema)
|
||||||
const client = new coreapi.Client(null, null, csrf)
|
const client = new coreapi.Client(null, null, csrf)
|
||||||
|
|
||||||
// Language Control
|
// Language Control
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for field in link.fields|with_location:'path' %}
|
{% for field in link.fields|with_location:'path' %}
|
||||||
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.description %}{{ field.description }}{% endif %}</td></tr>
|
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.schema.description %}{{ field.schema.description }}{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for field in link.fields|with_location:'query' %}
|
{% for field in link.fields|with_location:'query' %}
|
||||||
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.description %}{{ field.description }}{% endif %}</td></tr>
|
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.schema.description %}{{ field.schema.description }}{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for field in link.fields|with_location:'header' %}
|
{% for field in link.fields|with_location:'header' %}
|
||||||
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.description %}{{ field.description }}{% endif %}</td></tr>
|
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.schema.description %}{{ field.schema.description }}{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for field in link.fields|with_location:'body' %}
|
{% for field in link.fields|with_location:'body' %}
|
||||||
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.description %}{{ field.description }}{% endif %}</td></tr>
|
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.schema.description %}{{ field.schema.description }}{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for field in link.fields|with_location:'form' %}
|
{% for field in link.fields|with_location:'form' %}
|
||||||
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.description %}{{ field.description }}{% endif %}</td></tr>
|
<tr><td class="parameter-name"><code>{{ field.name }}</code>{% if field.required %} <span class="label label-warning">required</span>{% endif %}</td><td>{% if field.schema.description %}{{ field.schema.description }}{% endif %}</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -39,13 +39,8 @@ class FencedCodeExtension(markdown.Extension):
|
||||||
">normalize_whitespace")
|
">normalize_whitespace")
|
||||||
|
|
||||||
|
|
||||||
from pygments import highlight
|
|
||||||
from pygments.lexers import get_lexer_by_name
|
|
||||||
from pygments.formatters import HtmlFormatter
|
|
||||||
|
|
||||||
|
|
||||||
@register.tag(name='code')
|
@register.tag(name='code')
|
||||||
def do_code(parser,token):
|
def highlight_code(parser,token):
|
||||||
code = token.split_contents()[-1]
|
code = token.split_contents()[-1]
|
||||||
nodelist = parser.parse(('endcode',))
|
nodelist = parser.parse(('endcode',))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
|
@ -60,6 +55,9 @@ class CodeNode(template.Node):
|
||||||
self.nodelist = code
|
self.nodelist = code
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
|
from pygments import highlight
|
||||||
|
from pygments.lexers import get_lexer_by_name
|
||||||
|
from pygments.formatters import HtmlFormatter
|
||||||
body = self.nodelist.render(context)
|
body = self.nodelist.render(context)
|
||||||
lexer = get_lexer_by_name(self.lang, stripall=False)
|
lexer = get_lexer_by_name(self.lang, stripall=False)
|
||||||
formatter = HtmlFormatter(nowrap=True, style=self.style)
|
formatter = HtmlFormatter(nowrap=True, style=self.style)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user