mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-05 13:00:12 +03:00
commit
0b0a55c9a0
|
@ -1155,6 +1155,11 @@ The [html-json-forms][html-json-forms] package provides an algorithm and seriali
|
|||
|
||||
[DRF-Base64][drf-base64] provides a set of field and model serializers that handles the upload of base64-encoded files.
|
||||
|
||||
## QueryFields
|
||||
|
||||
[djangorestframework-queryfields][djangorestframework-queryfields] allows API clients to specify which fields will be sent in the response via inclusion or exclusion query paramaters.
|
||||
|
||||
|
||||
[cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion
|
||||
[relations]: relations.md
|
||||
[model-managers]: https://docs.djangoproject.com/en/stable/topics/db/managers/
|
||||
|
@ -1173,3 +1178,4 @@ The [html-json-forms][html-json-forms] package provides an algorithm and seriali
|
|||
[drf-dynamic-fields]: https://github.com/dbrgn/drf-dynamic-fields
|
||||
[drf-base64]: https://bitbucket.org/levit_scs/drf_base64
|
||||
[drf-serializer-extensions]: https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions
|
||||
[djangorestframework-queryfields]: http://djangorestframework-queryfields.readthedocs.io/
|
||||
|
|
|
@ -894,11 +894,11 @@ If the request is omitted from the context, the returned URLs will be of the for
|
|||
|
||||
The custom `X-Throttle-Wait-Second` header has now been dropped in favor of the standard `Retry-After` header. You can revert this behavior if needed by writing a custom exception handler for your application.
|
||||
|
||||
#### Date and time objects as ISO-8859-1 strings in serializer data.
|
||||
#### Date and time objects as ISO-8601 strings in serializer data.
|
||||
|
||||
Date and Time objects are now coerced to strings by default in the serializer output. Previously they were returned as `Date`, `Time` and `DateTime` objects, and later coerced to strings by the renderer.
|
||||
|
||||
You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8859-1'` will result in native objects being returned in serializer data.
|
||||
You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8601'` will result in native objects being returned in serializer data.
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
# Return native `Date` and `Time` objects in `serializer.data`
|
||||
|
|
|
@ -207,6 +207,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
|||
* [html-json-forms][html-json-forms] - Provides an algorithm and serializer to process HTML JSON Form submissions per the (inactive) spec.
|
||||
* [django-rest-framework-serializer-extensions][drf-serializer-extensions] -
|
||||
Enables black/whitelisting fields, and conditionally expanding child serializers on a per-view/request basis.
|
||||
* [djangorestframework-queryfields][djangorestframework-queryfields] - Serializer mixin allowing clients to control which fields will be sent in the API response.
|
||||
|
||||
### Serializer fields
|
||||
|
||||
|
@ -370,3 +371,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
|
|||
[drf_tweaks]: https://github.com/ArabellaTech/drf_tweaks
|
||||
[drf-oidc-auth]: https://github.com/ByteInternet/drf-oidc-auth
|
||||
[drf-serializer-extensions]: https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions
|
||||
[djangorestframework-queryfields]: https://github.com/wimglenn/djangorestframework-queryfields
|
||||
|
|
|
@ -48,6 +48,8 @@ We'll need to add our new `snippets` app and the `rest_framework` app to `INSTAL
|
|||
'snippets.apps.SnippetsConfig',
|
||||
)
|
||||
|
||||
Please note that if you're using Django <1.9, you need to replace `snippets.apps.SnippetsConfig` with `snippets`.
|
||||
|
||||
Okay, we're ready to roll.
|
||||
|
||||
## Creating a model to work with
|
||||
|
|
|
@ -294,7 +294,7 @@ class PageNumberPagination(BasePagination):
|
|||
name=self.page_query_param,
|
||||
required=False,
|
||||
location='query',
|
||||
description=force_text(self.page_query_description)
|
||||
#description=force_text(self.page_query_description)
|
||||
)
|
||||
]
|
||||
if self.page_size_query_param is not None:
|
||||
|
@ -303,7 +303,7 @@ class PageNumberPagination(BasePagination):
|
|||
name=self.page_size_query_param,
|
||||
required=False,
|
||||
location='query',
|
||||
description=force_text(self.page_size_query_description)
|
||||
#description=force_text(self.page_size_query_description)
|
||||
)
|
||||
)
|
||||
return fields
|
||||
|
@ -444,13 +444,13 @@ class LimitOffsetPagination(BasePagination):
|
|||
name=self.limit_query_param,
|
||||
required=False,
|
||||
location='query',
|
||||
description=force_text(self.limit_query_description)
|
||||
#description=force_text(self.limit_query_description)
|
||||
),
|
||||
coreapi.Field(
|
||||
name=self.offset_query_param,
|
||||
required=False,
|
||||
location='query',
|
||||
description=force_text(self.offset_query_description)
|
||||
#description=force_text(self.offset_query_description)
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -798,10 +798,30 @@ class DocumentationRenderer(BaseRenderer):
|
|||
media_type = 'text/html'
|
||||
format = 'html'
|
||||
charset = 'utf-8'
|
||||
template = 'rest_framework/docs/index.html'
|
||||
code_style = 'emacs'
|
||||
|
||||
def get_context(self, data):
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from django.utils.html import mark_safe
|
||||
formatter = HtmlFormatter(style=self.code_style)
|
||||
code_style = formatter.get_style_defs('.highlight')
|
||||
langs = ['shell', 'javascript', 'python']
|
||||
codec = coreapi.codecs.CoreJSONCodec()
|
||||
schema = mark_safe(codec.encode(data))
|
||||
return {
|
||||
'document': data,
|
||||
'langs': langs,
|
||||
'code_style': code_style,
|
||||
'schema': schema
|
||||
}
|
||||
|
||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||
from coredocs.main import render as render_docs
|
||||
return render_docs(data, theme='cerulean', highlight='emacs', static=lambda path: '/static/rest_framework/docs/' + path)
|
||||
#from coredocs.main import render as render_docs
|
||||
#return render_docs(data, theme='cerulean', highlight='emacs', static=lambda path: '/static/rest_framework/docs/' + path)
|
||||
template = loader.get_template(self.template)
|
||||
context = self.get_context(data)
|
||||
return template_render(template, context, request=renderer_context['request'])
|
||||
|
||||
|
||||
class MultiPartRenderer(BaseRenderer):
|
||||
|
|
|
@ -514,8 +514,8 @@ class SchemaGenerator(object):
|
|||
name=variable,
|
||||
location='path',
|
||||
required=True,
|
||||
title='' if (title is None) else title,
|
||||
description='' if (description is None) else description
|
||||
#title='' if (title is None) else title,
|
||||
#description='' if (description is None) else description
|
||||
)
|
||||
fields.append(field)
|
||||
|
||||
|
@ -540,7 +540,7 @@ class SchemaGenerator(object):
|
|||
name='data',
|
||||
location='body',
|
||||
required=True,
|
||||
type='array'
|
||||
#type='array'
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -559,11 +559,11 @@ class SchemaGenerator(object):
|
|||
name=field.field_name,
|
||||
location='form',
|
||||
required=required,
|
||||
title=title,
|
||||
description=description,
|
||||
type=types_lookup[field],
|
||||
input=determine_input(field),
|
||||
choices=getattr(field, 'choices', None)
|
||||
#title=title,
|
||||
#description=description,
|
||||
#type=types_lookup[field],
|
||||
#input=determine_input(field),
|
||||
#choices=getattr(field, 'choices', None)
|
||||
)
|
||||
fields.append(field)
|
||||
|
||||
|
|
19
rest_framework/templates/rest_framework/docs/document.html
Normal file
19
rest_framework/templates/rest_framework/docs/document.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% load rest_framework %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h1 id="document-title">{{ document.title }}</h1>
|
||||
<p>{% render_markdown document.description %}</p>
|
||||
</div>
|
||||
<div class="col-md-6"></div>
|
||||
</div>
|
||||
|
||||
{% for section_key, section in document.data.items %}
|
||||
<h1 id="{{ section_key }}" class="coredocs-section">{{ section_key }}</h1>
|
||||
{% for link_key, link in section.links.items %}
|
||||
{% include "rest_framework/docs/link.html" %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% for link_key, link in document.links.items %}
|
||||
{% include "rest_framework/docs/llink.html" %}
|
||||
{% endfor %}
|
143
rest_framework/templates/rest_framework/docs/index.html
Normal file
143
rest_framework/templates/rest_framework/docs/index.html
Normal file
|
@ -0,0 +1,143 @@
|
|||
{% load staticfiles %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>{{ document.title }}</title>
|
||||
|
||||
<link href="{% static 'rest_framework/docs/css/bootstrap-custom.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'rest_framework/docs/css/font-awesome-4.0.3.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'rest_framework/docs/css/base.css' %}" rel="stylesheet">
|
||||
<style>{{ code_style }}</style>
|
||||
<style>
|
||||
.highlight {background-color: #f7f7f9}
|
||||
.coredocs-link {border-top: 1px solid lightgrey; margin-top: 20px}
|
||||
.coredocs-section {border-top: 2px solid lightgrey; margin-top: 20px; padding-top: 20px}
|
||||
.checkbox label.control-label {font-weight: bold}
|
||||
@media (min-width: 768px) {
|
||||
.navbar-nav.navbar-right:last-child {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script src="https://unpkg.com/coreapi@0.0.17/dist/coreapi.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
{% include "rest_framework/docs/nav.html" %}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="col-md-2">{% include "rest_framework/docs/toc.html" %}</div>
|
||||
<div class="col-md-10" role="main">{% include "rest_framework/docs/document.html" %}</div>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'rest_framework/docs/js/jquery-1.10.2.min.js' %}"></script>
|
||||
<script src="{% static 'rest_framework/docs/js/bootstrap-3.0.3.min.js' %}"></script>
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
var cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = jQuery.trim(cookies[i]);
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
const csrf = {'X-CSRFToken': getCookie('csrftoken')}
|
||||
|
||||
const coreapi = window.coreapi
|
||||
const codec = new coreapi.codecs.CoreJSONCodec()
|
||||
const schema = {{ schema }}
|
||||
const doc = codec.decode(schema, {preloaded: true})
|
||||
const client = new coreapi.Client(null, null, csrf)
|
||||
|
||||
$('body').scrollspy({ target: '#toc' })
|
||||
|
||||
// Language Control
|
||||
$('.language-control li a').click(function (event) {
|
||||
event.preventDefault();
|
||||
var button = $(this)
|
||||
var language = button.data("language")
|
||||
|
||||
var languageControls = $('.language-control li a')
|
||||
languageControls.not('[data-language="' + language +'"]').parent().removeClass("active")
|
||||
languageControls.filter('[data-language="' + language +'"]').parent().addClass("active")
|
||||
|
||||
button.closest("li.dropdown").find('.dropdown-toggle span').text(language)
|
||||
|
||||
var codeBlocks = $('pre.highlight')
|
||||
codeBlocks.not('[data-language="' + language +'"]').addClass("hide")
|
||||
codeBlocks.filter('[data-language="' + language +'"]').removeClass("hide")
|
||||
})
|
||||
|
||||
// API Explorer
|
||||
|
||||
$('form').submit(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = $(this).closest("form");
|
||||
const key = form.data("key");
|
||||
var params = {};
|
||||
|
||||
const formData = new FormData(form.get()[0]);
|
||||
for (var [paramKey, paramValue] of formData.entries()) {
|
||||
var elem = form.find("[name=" + paramKey + "]")
|
||||
var dataType = elem.data('type') || 'string'
|
||||
var dataLocation = elem.data('location')
|
||||
|
||||
if (dataType === 'integer' && paramValue) {
|
||||
paramValue = parseInt(paramValue)
|
||||
} else if (dataType === 'number' && paramValue) {
|
||||
paramValue = parseFloat(paramValue)
|
||||
} else if (dataType === 'boolean' && paramValue) {
|
||||
paramValue = {
|
||||
'true': true,
|
||||
'false': false
|
||||
}[paramValue.toLowerCase()]
|
||||
} else if (dataType === 'array' && paramValue) {
|
||||
paramValue = JSON.parse(paramValue)
|
||||
}
|
||||
|
||||
if (dataLocation === 'query' && !paramValue) {
|
||||
continue
|
||||
}
|
||||
params[paramKey] = paramValue
|
||||
}
|
||||
|
||||
form.find(":checkbox").each(function( index ) {
|
||||
var name = $(this).attr("name");
|
||||
if (!params.hasOwnProperty(name)) {
|
||||
params[name] = false
|
||||
}
|
||||
})
|
||||
|
||||
console.log(params)
|
||||
|
||||
client.action(doc, key, params).then(function (data) {
|
||||
var response = JSON.stringify(data, null, 2);
|
||||
form.find(".response-data").text(response)
|
||||
form.find(".response-data").removeClass("hide")
|
||||
form.find(".response-error").addClass("hide")
|
||||
}).catch(function (error) {
|
||||
var response = JSON.stringify(error.content, null, 2);
|
||||
form.find(".response-error").text(error.message)
|
||||
form.find(".response-data").text(response)
|
||||
form.find(".response-error").removeClass("hide")
|
||||
form.find(".response-data").removeClass("hide")
|
||||
|
||||
/*form.find(".response-data").addClass("hide")*/
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
{% load rest_framework %}
|
||||
<pre class="highlight javascript hide" data-language="javascript"><code>{% code javascript %}var coreapi = window.coreapi
|
||||
|
||||
// Initialize a client & load the schema document
|
||||
var client = new coreapi.Client()
|
||||
var document = null
|
||||
client.get("{{ document.url }}").then(function(result) {
|
||||
document = result
|
||||
})
|
||||
|
||||
// Interact with the API endpoint
|
||||
var action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
|
||||
{% if link.fields %}var params = {
|
||||
{% for field in link.fields %} {{ field.name }}: ...{% if not loop.last %},{% endif %}
|
||||
{% endfor %}}
|
||||
{% endif %}client.action(document, action, params=params).then(function(result) {
|
||||
// Return value is in 'result'
|
||||
}){% endcode %}</code></pre>
|
|
@ -0,0 +1,13 @@
|
|||
{% load rest_framework %}
|
||||
<pre class="highlight python hide" data-language="python"><code>{% code python %}import coreapi
|
||||
|
||||
# Initialize a client & load the schema document
|
||||
client = coreapi.Client()
|
||||
document = client.get("{{ document.url }}"{% if schema_format %}, format="{{ schema_format }}"{% endif %})
|
||||
|
||||
# Interact with the API endpoint
|
||||
action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
|
||||
{% if link.fields %}params = {
|
||||
{% for field in link.fields %} "{{ field.name }}": ...{% if not loop.last %},{% endif %}
|
||||
{% endfor %}}
|
||||
{% endif %}result = client.action(document, action{% if link.fields %}, params=params{% endif %}){% endcode %}</code></pre>
|
|
@ -0,0 +1,6 @@
|
|||
{% load rest_framework %}
|
||||
<pre class="highlight shell" data-language="shell"><code>{% code bash %}# Load the schema document
|
||||
$ coreapi get {{ document.url }}{% if schema_format %} --format {{ schema_format }}{% endif %}
|
||||
|
||||
# Interact with the API endpoint
|
||||
$ coreapi action {% if section_key %}{{ section_key }} {% endif %}{{ link_key }}{% for field in link.fields %} -p {{ field.name }}=...{% endfor %}{% endcode %}</code></pre>
|
119
rest_framework/templates/rest_framework/docs/link.html
Normal file
119
rest_framework/templates/rest_framework/docs/link.html
Normal file
|
@ -0,0 +1,119 @@
|
|||
{% load rest_framework %}
|
||||
<div class="row coredocs-link">
|
||||
|
||||
<div class="col-md-6 docs-content">
|
||||
<button class="btn btn-success" style="float: right; margin-top: 20px" data-toggle="modal" data-target="#{{ section_key }}_{{ link_key }}_modal">Interact</button>
|
||||
|
||||
<h2 id="{{ section_key }}-{{ link_key }}">{{ link.title|default:link_key }}</h2>
|
||||
<p>
|
||||
<span class="label label-primary">{{ link.action|upper }}</span> <code>{{ link.url }}</code>
|
||||
</p>
|
||||
|
||||
<p>{% render_markdown link.description %}</p>
|
||||
|
||||
{% if link.fields|with_location:'path' %}
|
||||
<h4>Path Parameters</h4>
|
||||
<p>The following parameters should be included in the URL path.</p>
|
||||
<table class="parameters table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr><th>Parameter</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if link.fields|with_location:'query' %}
|
||||
<h4>Query Parameters</h4>
|
||||
<p>The following parameters should be included as part of a URL query string.</p>
|
||||
<table class="parameters table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr><th>Parameter</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if link.fields|with_location:'header' %}
|
||||
<h4>Header Parameters</h4>
|
||||
<p>The following parameters should be included as HTTP headers.</p>
|
||||
<table class="parameters table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr><th>Parameter</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if link.fields|with_location:'body' %}
|
||||
<h4>Request Body</h4>
|
||||
<p>The request body should be <code>"{{ link.encoding }}"</code> encoded, and should contain a single item.</p>
|
||||
<table class="parameters table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr><th>Parameter</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif link.fields|with_location:'form' %}
|
||||
<h4>Request Body</h4>
|
||||
<p>The request body should be a <code>"{{ link.encoding }}"</code> encoded object, containing the following items.</p>
|
||||
<table class="parameters table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr><th>Parameter</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% 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>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 code-samples">
|
||||
{% if 'shell' in langs %}{% include "rest_framework/docs/langs/shell.html" %}{% endif %}
|
||||
{% if 'python' in langs %}{% include "rest_framework/docs/langs/python.html" %}{% endif %}
|
||||
{% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript.html" %}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade api-modal" id="{{ section_key }}_{{ link_key }}_modal" tabindex="-1" role="dialog" aria-labelledby="api explorer modal">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">{{ link.title|default:link_key }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form data-key='["{{ section_key }}", "{{ link_key }}"]'>
|
||||
{% form_for_link link %}
|
||||
|
||||
<div id="response">
|
||||
<div class="response-error alert alert-danger hide"></div>
|
||||
<pre class="response-data hide"></pre>
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
34
rest_framework/templates/rest_framework/docs/nav.html
Normal file
34
rest_framework/templates/rest_framework/docs/nav.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Collapsed navigation -->
|
||||
<div class="navbar-header">
|
||||
<!-- Expander button -->
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
|
||||
<!-- Main title -->
|
||||
<a class="navbar-brand" href="{{ homepage_url }}">{{ document.title }}</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span>shell</span> <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu language-control">
|
||||
<li class="active"><a href="#" data-language="shell">shell</a></li>
|
||||
<li><a href="#" data-language="javascript">javascript</a></li>
|
||||
<li><a href="#" data-language="python">python</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
17
rest_framework/templates/rest_framework/docs/toc.html
Normal file
17
rest_framework/templates/rest_framework/docs/toc.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div id="toc" class="bs-sidebar hidden-print affix" role="complementary">
|
||||
<ul class="nav bs-sidenav">
|
||||
<li class="active">
|
||||
<a href="#document-title">{{ document.title }}</a>
|
||||
</li>
|
||||
{% for section_key, section in document.data.items %}
|
||||
<li>
|
||||
<a href="#{{ section_key }}">{{ section_key }}</a>
|
||||
<ul class="nav">
|
||||
{% for link_key, link in section.links.items %}
|
||||
<li><a href="#{{ section_key }}-{{ link_key }}">{{ link.title|default:link_key }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
|
@ -13,12 +13,89 @@ from rest_framework.compat import NoReverseMatch, reverse, template_render
|
|||
from rest_framework.renderers import HTMLFormRenderer
|
||||
from rest_framework.utils.urls import replace_query_param
|
||||
|
||||
from markdown.extensions.fenced_code import FencedBlockPreprocessor
|
||||
import markdown
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
# Regex for adding classes to html snippets
|
||||
class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
|
||||
|
||||
|
||||
class CustomFencedBlockPreprocessor(FencedBlockPreprocessor):
|
||||
CODE_WRAP = '<pre%s><code>%s</code></pre>'
|
||||
LANG_TAG = ' class="highlight %s"'
|
||||
|
||||
|
||||
class FencedCodeExtension(markdown.Extension):
|
||||
|
||||
def extendMarkdown(self, md, md_globals):
|
||||
""" Add FencedBlockPreprocessor to the Markdown instance. """
|
||||
md.registerExtension(self)
|
||||
|
||||
md.preprocessors.add('fenced_code_block',
|
||||
CustomFencedBlockPreprocessor(md),
|
||||
">normalize_whitespace")
|
||||
|
||||
|
||||
from pygments import highlight
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
from pygments.formatters import HtmlFormatter
|
||||
|
||||
|
||||
@register.tag(name='code')
|
||||
def do_code(parser,token):
|
||||
code = token.split_contents()[-1]
|
||||
nodelist = parser.parse(('endcode',))
|
||||
parser.delete_first_token()
|
||||
return CodeNode(code, nodelist)
|
||||
|
||||
|
||||
class CodeNode(template.Node):
|
||||
style = 'emacs'
|
||||
|
||||
def __init__(self, lang, code):
|
||||
self.lang = lang
|
||||
self.nodelist = code
|
||||
|
||||
def render(self, context):
|
||||
body = self.nodelist.render(context)
|
||||
lexer = get_lexer_by_name(self.lang, stripall=False)
|
||||
formatter = HtmlFormatter(nowrap=True, style=self.style)
|
||||
code = highlight(body, lexer, formatter)
|
||||
return code
|
||||
|
||||
|
||||
@register.filter()
|
||||
def with_location(fields, location):
|
||||
return [
|
||||
field for field in fields
|
||||
if field.location == location
|
||||
]
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def form_for_link(link):
|
||||
import coreschema
|
||||
properties = {
|
||||
field.name: field.schema or coreschema.String()
|
||||
for field in link.fields
|
||||
}
|
||||
required = [
|
||||
field.name
|
||||
for field in link.fields
|
||||
if field.required
|
||||
]
|
||||
schema = coreschema.Object(properties=properties, required=required)
|
||||
return coreschema.render_to_form(schema)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def render_markdown(markdown_text):
|
||||
return markdown.markdown(markdown_text, extensions=[FencedCodeExtension(), "tables"])
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_pagination_html(pager):
|
||||
return pager.to_html()
|
||||
|
|
Loading…
Reference in New Issue
Block a user