Docs: fix code block style

This commit is contained in:
Shyamkumar Yadav 2022-10-15 13:41:31 +05:30
parent 9407833a83
commit 3fa4affce2
12 changed files with 269 additions and 343 deletions

View File

@ -16,48 +16,46 @@ decorators with class based views. This can be used with
other cache decorators such as [`cache_page`][page],
[`vary_on_cookie`][cookie] and [`vary_on_headers`][headers].
```python
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import viewsets
class UserViewSet(viewsets.ViewSet):
# With cookie: cache requested url for each user for 2 hours
@method_decorator(cache_page(60*60*2))
@method_decorator(vary_on_cookie)
def list(self, request, format=None):
content = {
'user_feed': request.user.get_user_feed()
}
return Response(content)
class UserViewSet(viewsets.ViewSet):
# With cookie: cache requested url for each user for 2 hours
@method_decorator(cache_page(60*60*2))
@method_decorator(vary_on_cookie)
def list(self, request, format=None):
content = {
'user_feed': request.user.get_user_feed()
}
return Response(content)
class ProfileView(APIView):
# With auth: cache requested url for each user for 2 hours
@method_decorator(cache_page(60*60*2))
@method_decorator(vary_on_headers("Authorization",))
def get(self, request, format=None):
content = {
'user_feed': request.user.get_user_feed()
}
return Response(content)
class ProfileView(APIView):
# With auth: cache requested url for each user for 2 hours
@method_decorator(cache_page(60*60*2))
@method_decorator(vary_on_headers("Authorization",))
def get(self, request, format=None):
content = {
'user_feed': request.user.get_user_feed()
}
return Response(content)
class PostView(APIView):
# Cache page for the requested url
@method_decorator(cache_page(60*60*2))
def get(self, request, format=None):
content = {
'title': 'Post title',
'body': 'Post content'
}
return Response(content)
```
class PostView(APIView):
# Cache page for the requested url
@method_decorator(cache_page(60*60*2))
def get(self, request, format=None):
content = {
'title': 'Post title',
'body': 'Post content'
}
return Response(content)
**NOTE:** The [`cache_page`][page] decorator only caches the
`GET` and `HEAD` responses with status 200.

View File

@ -107,9 +107,7 @@ The TemplateHTMLRenderer will create a `RequestContext`, using the `response.dat
**Note:** When used with a view that makes use of a serializer the `Response` sent for rendering may not be a dictionary and will need to be wrapped in a dict before returning to allow the TemplateHTMLRenderer to render it. For example:
```
response.data = {'results': response.data}
```
response.data = {'results': response.data}
---

View File

@ -48,9 +48,7 @@ The following sections explain more.
If your schema is static, you can use the `generateschema` management command:
```bash
./manage.py generateschema --file openapi-schema.yml
```
./manage.py generateschema --file openapi-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
@ -69,22 +67,20 @@ To route a `SchemaView`, use the `get_schema_view()` helper.
In `urls.py`:
```python
from rest_framework.schemas import get_schema_view
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 …",
version="1.0.0"
), name='openapi-schema'),
# ...
]
```
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 …",
version="1.0.0"
), name='openapi-schema'),
# ...
]
#### `get_schema_view()`
@ -139,9 +135,7 @@ The `get_schema_view()` helper takes the following keyword arguments:
**Schema-level customization**
```python
from rest_framework.schemas.openapi import SchemaGenerator
```
from rest_framework.schemas.openapi import SchemaGenerator
`SchemaGenerator` is a class that walks a list of routed URL patterns, requests
the schema for each view and collates the resulting OpenAPI schema.
@ -179,21 +173,17 @@ This is a good point to override if you want to customize the generated
dictionary For example you might wish to add terms of service to the [top-level
`info` object][info-object]:
```
class TOSSchemaGenerator(SchemaGenerator):
def get_schema(self, *args, **kwargs):
schema = super().get_schema(*args, **kwargs)
schema["info"]["termsOfService"] = "https://example.com/tos.html"
return schema
```
class TOSSchemaGenerator(SchemaGenerator):
def get_schema(self, *args, **kwargs):
schema = super().get_schema(*args, **kwargs)
schema["info"]["termsOfService"] = "https://example.com/tos.html"
return schema
## AutoSchema
**Per-View Customization**
```python
from rest_framework.schemas.openapi import AutoSchema
```
from rest_framework.schemas.openapi import AutoSchema
By default, view introspection is performed by an `AutoSchema` instance
accessible via the `schema` attribute on `APIView`.
@ -209,10 +199,8 @@ and path:
the endpoint, including path and query parameters for pagination, filtering,
and so on.
```python
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)
```
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)
In compiling the schema, `SchemaGenerator` calls `get_components()` and
`get_operation()` for each view, allowed method, and path.
@ -236,17 +224,15 @@ Keeping with this pattern, try not to let schema logic leak into your own
views, serializers, or fields when customizing the schema generation. You might
be tempted to do something like this:
```python
class CustomSchema(AutoSchema):
"""
AutoSchema subclass using schema_extra_info on the view.
"""
...
class CustomSchema(AutoSchema):
"""
AutoSchema subclass using schema_extra_info on the view.
"""
...
class CustomView(APIView):
schema = CustomSchema()
schema_extra_info = ... some extra info ...
```
class CustomView(APIView):
schema = CustomSchema()
schema_extra_info = ... some extra info ...
Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
view. This is _OK_ (it doesn't actually hurt) but it means you'll end up with
@ -255,19 +241,17 @@ your schema logic spread out in a number of different places.
Instead try to subclass `AutoSchema` such that the `extra_info` doesn't leak
out into the view:
```python
class BaseSchema(AutoSchema):
"""
AutoSchema subclass that knows how to use extra_info.
"""
...
class BaseSchema(AutoSchema):
"""
AutoSchema subclass that knows how to use extra_info.
"""
...
class CustomSchema(BaseSchema):
extra_info = ... some extra info ...
class CustomSchema(BaseSchema):
extra_info = ... some extra info ...
class CustomView(APIView):
schema = CustomSchema()
```
class CustomView(APIView):
schema = CustomSchema()
This style is slightly more verbose but maintains the encapsulation of the
schema related code. It's more _cohesive_ in the _parlance_. It'll keep the
@ -277,18 +261,16 @@ If an option applies to many view classes, rather than creating a specific
subclass per-view, you may find it more convenient to allow specifying the
option as an `__init__()` kwarg to your base `AutoSchema` subclass:
```python
class CustomSchema(BaseSchema):
def __init__(self, **kwargs):
# store extra_info for later
self.extra_info = kwargs.pop("extra_info")
super().__init__(**kwargs)
class CustomSchema(BaseSchema):
def __init__(self, **kwargs):
# store extra_info for later
self.extra_info = kwargs.pop("extra_info")
super().__init__(**kwargs)
class CustomView(APIView):
schema = CustomSchema(
extra_info=... some extra info ...
)
```
class CustomView(APIView):
schema = CustomSchema(
extra_info=... some extra info ...
)
This saves you having to create a custom subclass per-view for a commonly used option.
@ -333,15 +315,13 @@ will handle the default fields that Django REST Framework provides.
For `SerializerMethodField` instances, for which the schema is unknown, or custom field subclasses you should override `map_field()` to generate the correct schema:
```python
class CustomSchema(AutoSchema):
"""Extension of ``AutoSchema`` to add support for custom field schemas."""
class CustomSchema(AutoSchema):
"""Extension of ``AutoSchema`` to add support for custom field schemas."""
def map_field(self, field):
# Handle SerializerMethodFields or custom fields here...
# ...
return super().map_field(field)
```
def map_field(self, field):
# Handle SerializerMethodFields or custom fields here...
# ...
return super().map_field(field)
Authors of third-party packages should aim to provide an `AutoSchema` subclass,
and a mixin, overriding `map_field()` so that users can easily generate schemas
@ -407,15 +387,13 @@ The available kwargs are:
You pass the kwargs when declaring the `AutoSchema` instance on your view:
```
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
schema = AutoSchema(
tags=['Pets'],
component_name='Pet',
operation_id_base='Pet',
)
...
```
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
schema = AutoSchema(
tags=['Pets'],
component_name='Pet',
operation_id_base='Pet',
)
...
Assuming a `Pet` model and `PetSerializer` serializer, the kwargs in this
example are probably not needed. Often, though, you'll need to pass the kwargs

View File

@ -174,16 +174,16 @@ A more complete example of extra actions:
The `action` decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example:
@action(detail=True, methods=['post', 'delete'])
def unset_password(self, request, pk=None):
...
@action(detail=True, methods=['post', 'delete'])
def unset_password(self, request, pk=None):
...
The decorator allows you to override any viewset-level configuration such as `permission_classes`, `serializer_class`, `filter_backends`...:
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$`. Use the `url_path` and `url_name` parameters to change the URL segment and the reverse URL name of the action.
@ -193,7 +193,6 @@ To view all extra actions, call the `.get_extra_actions()` method.
Extra actions can map additional HTTP methods to separate `ViewSet` methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
```python
@action(detail=True, methods=['put'], name='Change Password')
def password(self, request, pk=None):
"""Update the user's password."""
@ -203,7 +202,6 @@ Extra actions can map additional HTTP methods to separate `ViewSet` methods. For
def delete_password(self, request, pk=None):
"""Delete the user's password."""
...
```
## Reversing action URLs
@ -213,17 +211,13 @@ Note that the `basename` is provided by the router during `ViewSet` registration
Using the example from the previous section:
```python
>>> view.reverse_action('set-password', args=['1'])
'http://localhost:8000/api/users/1/set_password'
```
>>> view.reverse_action('set-password', args=['1'])
'http://localhost:8000/api/users/1/set_password'
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
```python
>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
```
>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
The `url_name` argument for `.reverse_action()` should match the same argument to the `@action` decorator. Additionally, this method can be used to reverse the default actions, such as `list` and `create`.

View File

@ -39,12 +39,10 @@ update your REST framework settings to include `DEFAULT_SCHEMA_CLASS` explicitly
**settings.py**:
```python
REST_FRAMEWORK = {
...
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
}
```
REST_FRAMEWORK = {
...
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
}
You'll still be able to keep using CoreAPI schemas, API docs, and client for the
foreseeable future. We'll aim to ensure that the CoreAPI schema generator remains
@ -66,21 +64,19 @@ shortcut.
In your `urls.py`:
```python
from rest_framework.schemas import get_schema_view
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'),
# ...
]
```
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'),
# ...
]
### Customization

View File

@ -41,20 +41,18 @@ include:
In this example view operation descriptions for the `get` and `post` methods will
be extracted from the class docstring:
```python
class DocStringExampleListView(APIView):
"""
get: A description of my GET operation.
post: A description of my POST operation.
"""
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
class DocStringExampleListView(APIView):
"""
get: A description of my GET operation.
post: A description of my POST operation.
"""
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get(self, request, *args, **kwargs):
...
def get(self, request, *args, **kwargs):
...
def post(self, request, *args, **kwargs):
...
```
def post(self, request, *args, **kwargs):
...
## Validator / Default Context
@ -71,23 +69,19 @@ The `__call__` method should then include an additional `serializer_field` argum
Validator implementations will look like this:
```python
class CustomValidator:
requires_context = True
class CustomValidator:
requires_context = True
def __call__(self, value, serializer_field):
...
```
def __call__(self, value, serializer_field):
...
Default implementations will look like this:
```python
class CustomDefault:
requires_context = True
class CustomDefault:
requires_context = True
def __call__(self, serializer_field):
...
```
def __call__(self, serializer_field):
...
---

View File

@ -39,11 +39,9 @@ Method | Path | Tags
The tags used for a particular view may also be overridden...
```python
class MyOrders(APIView):
schema = AutoSchema(tags=['users', 'orders'])
...
```
class MyOrders(APIView):
schema = AutoSchema(tags=['users', 'orders'])
...
See [the schema documentation](https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags) for more information.
@ -66,10 +64,8 @@ The names used for a component default to using the serializer class name, [but
may be overridden if needed](https://www.django-rest-framework.org/api-guide/schemas/#components
)...
```python
class MyOrders(APIView):
schema = AutoSchema(component_name="OrderDetails")
```
class MyOrders(APIView):
schema = AutoSchema(component_name="OrderDetails")
## More Public API
@ -111,35 +107,31 @@ The class now supports nested search within `JSONField` and `HStoreField`, using
the double underscore notation for traversing which element of the field the
search should apply to.
```python
class SitesSearchView(generics.ListAPIView):
"""
An API view to return a list of archaeological sites, optionally filtered
by a search against the site name or location. (Location searches are
matched against the region and country names.)
"""
queryset = Sites.objects.all()
serializer_class = SitesSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['site_name', 'location__region', 'location__country']
```
class SitesSearchView(generics.ListAPIView):
"""
An API view to return a list of archaeological sites, optionally filtered
by a search against the site name or location. (Location searches are
matched against the region and country names.)
"""
queryset = Sites.objects.all()
serializer_class = SitesSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['site_name', 'location__region', 'location__country']
### Searches against annotate fields
Django allows querysets to create additional virtual fields, using the `.annotate`
method. We now support searching against annotate fields.
```python
class PublisherSearchView(generics.ListAPIView):
"""
Search for publishers, optionally filtering the search against the average
rating of all their books.
"""
queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
serializer_class = PublisherSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['avg_rating']
```
class PublisherSearchView(generics.ListAPIView):
"""
Search for publishers, optionally filtering the search against the average
rating of all their books.
"""
queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
serializer_class = PublisherSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['avg_rating']
---

View File

@ -40,15 +40,11 @@ From REST framework 3.13 onwards, this is now *explicitly enforced*.
The most feasible cases where users might be accidentally omitting the keyword arguments
are likely in the composite fields, `ListField` and `DictField`. For instance...
```python
aliases = serializers.ListField(serializers.CharField())
```
aliases = serializers.ListField(serializers.CharField())
They must now use the more explicit keyword argument style...
```python
aliases = serializers.ListField(child=serializers.CharField())
```
aliases = serializers.ListField(child=serializers.CharField())
This change has been made because using positional arguments here *does not* result in the expected behaviour.

View File

@ -59,20 +59,18 @@ For example, to include a swagger schema to your API, you would do the following
* Include the schema view in your URL conf:
```py
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
schema_view = get_schema_view(
title='Example API',
renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
)
schema_view = get_schema_view(
title='Example API',
renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
)
urlpatterns = [
path('swagger/', schema_view),
...
]
```
urlpatterns = [
path('swagger/', schema_view),
...
]
There have been a large number of fixes to the schema generation. These should
resolve issues for anyone using the latest version of the `django-rest-swagger`

View File

@ -59,28 +59,24 @@ the schema into your repository.
Here's an example of adding an OpenAPI schema to the URL conf:
```python
from rest_framework.schemas import get_schema_view
from rest_framework.renderers import JSONOpenAPIRenderer
from django.urls import path
from rest_framework.schemas import get_schema_view
from rest_framework.renderers import JSONOpenAPIRenderer
from django.urls import path
schema_view = get_schema_view(
title='Server Monitoring API',
url='https://www.example.org/api/',
renderer_classes=[JSONOpenAPIRenderer]
)
schema_view = get_schema_view(
title='Server Monitoring API',
url='https://www.example.org/api/',
renderer_classes=[JSONOpenAPIRenderer]
)
urlpatterns = [
path('schema.json', schema_view),
...
]
```
urlpatterns = [
path('schema.json', schema_view),
...
]
And here's how you can use the `generateschema` management command:
```shell
$ python manage.py generateschema --format openapi > schema.yml
```
$ python manage.py generateschema --format openapi > schema.yml
There's lots of different tooling that you can use for working with OpenAPI
schemas. One option that we're working on is the [API Star](https://docs.apistar.com/)
@ -88,17 +84,13 @@ command line tool.
You can use `apistar` to validate your API schema:
```shell
$ apistar validate --path schema.json --format openapi
✓ Valid OpenAPI schema.
```
$ apistar validate --path schema.json --format openapi
✓ Valid OpenAPI schema.
Or to build API documentation:
```shell
$ apistar docs --path schema.json --format openapi
✓ Documentation built at "build/index.html".
```
$ apistar docs --path schema.json --format openapi
✓ Documentation built at "build/index.html".
API Star also includes a [dynamic client library](https://docs.apistar.com/client-library/)
that uses an API schema to automatically provide a client library interface for making requests.
@ -109,9 +101,7 @@ You can now compose permission classes using the and/or operators, `&` and `|`.
For example...
```python
permission_classes = [IsAuthenticated & (ReadOnly | IsAdminUser)]
```
permission_classes = [IsAuthenticated & (ReadOnly | IsAdminUser)]
If you're using custom permission classes then make sure that you are subclassing
from `BasePermission` in order to enable this support.

View File

@ -301,12 +301,12 @@ Be sure to upgrade to Python 3 before upgrading to Django REST Framework 3.10.
* Deprecate the `Router.get_default_base_name` method in favor of `Router.get_default_basename`. [#5990][gh5990]
* Change `CharField` to disallow null bytes. [#6073][gh6073]
To revert to the old behavior, subclass `CharField` and remove `ProhibitNullCharactersValidator` from the validators.
```python
class NullableCharField(serializers.CharField):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
```
class NullableCharField(serializers.CharField):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
* Allow permissions to be composed [#5753][gh5753]

View File

@ -25,53 +25,49 @@ 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:
```html
<!DOCTYPE html>
<html>
<head>
<title>Swagger</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({
url: "{% url schema_url %}",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",
requestInterceptor: (request) => {
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
return request;
}
})
</script>
</body>
</html>
```
<!DOCTYPE html>
<html>
<head>
<title>Swagger</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({
url: "{% url schema_url %}",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",
requestInterceptor: (request) => {
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
return request;
}
})
</script>
</body>
</html>
Save this in your templates folder as `swagger-ui.html`. Then route a
`TemplateView` in your project's URL conf:
```python
from django.views.generic import TemplateView
from django.views.generic import TemplateView
urlpatterns = [
# ...
# Route TemplateView to serve Swagger UI template.
# * Provide `extra_context` with view name of `SchemaView`.
path('swagger-ui/', TemplateView.as_view(
template_name='swagger-ui.html',
extra_context={'schema_url':'openapi-schema'}
), name='swagger-ui'),
]
```
urlpatterns = [
# ...
# Route TemplateView to serve Swagger UI template.
# * Provide `extra_context` with view name of `SchemaView`.
path('swagger-ui/', TemplateView.as_view(
template_name='swagger-ui.html',
extra_context={'schema_url':'openapi-schema'}
), name='swagger-ui'),
]
See the [Swagger UI documentation][swagger-ui] for advanced usage.
@ -81,46 +77,42 @@ Assuming you've followed the example from the schemas documentation for routing
a dynamic `SchemaView`, a minimal Django template for using ReDoc might be
this:
```html
<!DOCTYPE html>
<html>
<head>
<title>ReDoc</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!-- ReDoc doesn't change outer page styles -->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='{% url schema_url %}'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>
```
<!DOCTYPE html>
<html>
<head>
<title>ReDoc</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!-- ReDoc doesn't change outer page styles -->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='{% url schema_url %}'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>
Save this in your templates folder as `redoc.html`. Then route a `TemplateView`
in your project's URL conf:
```python
from django.views.generic import TemplateView
from django.views.generic import TemplateView
urlpatterns = [
# ...
# Route TemplateView to serve the ReDoc template.
# * Provide `extra_context` with view name of `SchemaView`.
path('redoc/', TemplateView.as_view(
template_name='redoc.html',
extra_context={'schema_url':'openapi-schema'}
), name='redoc'),
]
```
urlpatterns = [
# ...
# Route TemplateView to serve the ReDoc template.
# * Provide `extra_context` with view name of `SchemaView`.
path('redoc/', TemplateView.as_view(
template_name='redoc.html',
extra_context={'schema_url':'openapi-schema'}
), name='redoc'),
]
See the [ReDoc documentation][redoc] for advanced usage.