Skip to content

Commit a540acd

Browse files
Allowed customising API documentation code samples (#5752)
* Allowed additional languages in API documentation * Documented renderer_classes parameter and customising languages.
1 parent 588b61e commit a540acd

File tree

9 files changed

+49
-19
lines changed

9 files changed

+49
-19
lines changed

docs/topics/documenting-your-api.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ The `rest_framework.documentation` module provides three helper functions to hel
107107
* `generator_class`: Default `rest_framework.schemas.SchemaGenerator`. May be used to specify a `SchemaGenerator` subclass to be passed to the `SchemaView`.
108108
* `authentication_classes`: Default `api_settings.DEFAULT_AUTHENTICATION_CLASSES`. May be used to pass custom authentication classes to the `SchemaView`.
109109
* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES` May be used to pass custom permission classes to the `SchemaView`.
110+
* `renderer_classes`: Default `None`. May be used to pass custom renderer classes to the `SchemaView`.
110111

111112
#### `get_docs_view`
112113

@@ -117,7 +118,8 @@ The `rest_framework.documentation` module provides three helper functions to hel
117118
* `patterns`: Default `None`. A list of URLs to inspect when generating the schema. If `None` project's URL conf will be used.
118119
* `generator_class`: Default `rest_framework.schemas.SchemaGenerator`. May be used to specify a `SchemaGenerator` subclass to be passed to the `SchemaView`.
119120
* `authentication_classes`: Default `api_settings.DEFAULT_AUTHENTICATION_CLASSES`. May be used to pass custom authentication classes to the `SchemaView`.
120-
* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES` May be used to pass custom permission classes to the `SchemaView`.
121+
* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES`. May be used to pass custom permission classes to the `SchemaView`.
122+
* `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.
121123

122124
#### `get_schemajs_view`
123125

@@ -130,6 +132,25 @@ The `rest_framework.documentation` module provides three helper functions to hel
130132
* `authentication_classes`: Default `api_settings.DEFAULT_AUTHENTICATION_CLASSES`. May be used to pass custom authentication classes to the `SchemaView`.
131133
* `permission_classes`: Default `api_settings.DEFAULT_PERMISSION_CLASSES` May be used to pass custom permission classes to the `SchemaView`.
132134

135+
136+
### Customising code samples
137+
138+
The built-in API documentation includes automatically generated code samples for
139+
each of the available API client libraries.
140+
141+
You may customise these samples by subclassing `DocumentationRenderer`, setting
142+
`languages` to the list of languages you wish to support:
143+
144+
from rest_framework.renderers import DocumentationRenderer
145+
146+
147+
class CustomRenderer(DocumentationRenderer):
148+
languages = ['ruby', 'go']
149+
150+
For each language you need to provide an `intro` template, detailing installation instructions and such,
151+
plus a generic template for making API requests, that can be filled with individual request details.
152+
See the [templates for the bundled languages][client-library-templates] for examples.
153+
133154
---
134155

135156
## Third party packages
@@ -138,11 +159,11 @@ There are a number of mature third-party packages for providing API documentatio
138159

139160
#### drf-yasg - Yet Another Swagger Generator
140161

141-
[drf-yasg][drf-yasg] is a [Swagger][swagger] generation tool implemented without using the schema generation provided
162+
[drf-yasg][drf-yasg] is a [Swagger][swagger] generation tool implemented without using the schema generation provided
142163
by Django Rest Framework.
143164

144-
It aims to implement as much of the [OpenAPI][open-api] specification as possible - nested schemas, named models,
145-
response bodies, enum/pattern/min/max validators, form parameters, etc. - and to generate documents usable with code
165+
It aims to implement as much of the [OpenAPI][open-api] specification as possible - nested schemas, named models,
166+
response bodies, enum/pattern/min/max validators, form parameters, etc. - and to generate documents usable with code
146167
generation tools like `swagger-codegen`.
147168

148169
This also translates into a very useful interactive documentation viewer in the form of `swagger-ui`:
@@ -314,3 +335,4 @@ To implement a hypermedia API you'll need to decide on an appropriate media type
314335
[image-self-describing-api]: ../img/self-describing.png
315336
[schemas-examples]: ../api-guide/schemas/#example
316337
[metadata-docs]: ../api-guide/metadata/
338+
[client-library-templates]: https://github.com/encode/django-rest-framework/tree/master/rest_framework/templates/rest_framework/docs/langs

rest_framework/documentation.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ def get_docs_view(
1111
title=None, description=None, schema_url=None, public=True,
1212
patterns=None, generator_class=SchemaGenerator,
1313
authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES,
14-
permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES):
15-
renderer_classes = [DocumentationRenderer, CoreJSONRenderer]
14+
permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES,
15+
renderer_classes=None):
16+
17+
if renderer_classes is None:
18+
renderer_classes = [DocumentationRenderer, CoreJSONRenderer]
1619

1720
return get_schema_view(
1821
title=title,
@@ -51,7 +54,8 @@ def include_docs_urls(
5154
title=None, description=None, schema_url=None, public=True,
5255
patterns=None, generator_class=SchemaGenerator,
5356
authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES,
54-
permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES):
57+
permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES,
58+
renderer_classes=None):
5559
docs_view = get_docs_view(
5660
title=title,
5761
description=description,
@@ -60,6 +64,7 @@ def include_docs_urls(
6064
patterns=patterns,
6165
generator_class=generator_class,
6266
authentication_classes=authentication_classes,
67+
renderer_classes=renderer_classes,
6368
permission_classes=permission_classes,
6469
)
6570
schema_js_view = get_schemajs_view(

rest_framework/renderers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,8 @@ def get_context(self, data, request):
830830
return {
831831
'document': data,
832832
'langs': self.languages,
833+
'lang_htmls': ["rest_framework/docs/langs/%s.html" % l for l in self.languages],
834+
'lang_intro_htmls': ["rest_framework/docs/langs/%s-intro.html" % l for l in self.languages],
833835
'code_style': pygments_css(self.code_style),
834836
'request': request
835837
}

rest_framework/templates/rest_framework/docs/document.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ <h1>{{ document.title }}</h1>
88
{% endif %}
99
</div>
1010
<div class="col-md-6 intro-code">
11-
{% if 'shell' in langs %}{% include "rest_framework/docs/langs/shell-intro.html" %}{% endif %}
12-
{% if 'python' in langs %}{% include "rest_framework/docs/langs/python-intro.html" %}{% endif %}
13-
{% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript-intro.html" %}{% endif %}
11+
{% for html in lang_intro_htmls %}
12+
{% include html %}
13+
{% endfor %}
1414
</div>
1515
</div>
1616
{% if document|data %}

rest_framework/templates/rest_framework/docs/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
$('#auth-control').children().removeClass('active');
5252
$('#auth-control').find("[data-auth='session']").closest('li').addClass('active');
5353
{% endif %}
54+
$('pre.highlight').filter('[data-language="{{ langs | first }}"]').removeClass('hide');
5455
</script>
5556
</body>
5657
</html>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{% load rest_framework %}
2-
<pre class="highlight shell" data-language="shell"><code>{% code bash %}# Install the command line client
2+
<pre class="highlight shell hide" data-language="shell"><code>{% code bash %}# Install the command line client
33
$ pip install coreapi-cli{% endcode %}</code></pre>

rest_framework/templates/rest_framework/docs/langs/shell.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{% load rest_framework %}
2-
<pre class="highlight shell" data-language="shell"><code>{% code bash %}# Load the schema document
2+
<pre class="highlight shell hide" data-language="shell"><code>{% code bash %}# Load the schema document
33
$ coreapi get {{ document.url }}{% if schema_format %} --format {{ schema_format }}{% endif %}
44

55
# Interact with the API endpoint

rest_framework/templates/rest_framework/docs/link.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ <h4>Request Body</h4>
9393
</div>
9494

9595
<div class="col-md-6 code-samples">
96-
{% if 'shell' in langs %}{% include "rest_framework/docs/langs/shell.html" %}{% endif %}
97-
{% if 'python' in langs %}{% include "rest_framework/docs/langs/python.html" %}{% endif %}
98-
{% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript.html" %}{% endif %}
96+
{% for html in lang_htmls %}
97+
{% include html %}
98+
{% endfor %}
9999
</div>
100100
</div>
101101

rest_framework/templates/rest_framework/docs/sidebar.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ <h3 class="brand"><a href="#">{{ document.title }}</a></h3>
3131
</ul>
3232

3333
<li data-toggle="collapse" data-target="#language-control" class="collapsed">
34-
<a><i class="fa fa-code fa-lg"></i> Source Code</a> <span id="selected-language">shell</span>
34+
<a><i class="fa fa-code fa-lg"></i> Source Code</a> <span id="selected-language">{{ langs | first }}</span>
3535
</li>
3636
<ul class="sub-menu collapse out" id="language-control">
37-
<li class="active"><a href="#" data-language="shell">shell</a></li>
38-
<li><a href="#" data-language="javascript">javascript</a></li>
39-
<li><a href="#" data-language="python">python</a></li>
37+
{% for lang in langs %}
38+
<li{% if loop.first %} class="active"{% endif %}><a href="#" data-language="{{ lang }}">{{ lang }}</a></li>
39+
{% endfor %}
4040
</ul>
4141
</ul>
4242

0 commit comments

Comments
 (0)