mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-25 23:50:01 +03:00
Docs: fix code block style
This commit is contained in:
parent
9407833a83
commit
3fa4affce2
|
@ -16,48 +16,46 @@ decorators with class based views. This can be used with
|
||||||
other cache decorators such as [`cache_page`][page],
|
other cache decorators such as [`cache_page`][page],
|
||||||
[`vary_on_cookie`][cookie] and [`vary_on_headers`][headers].
|
[`vary_on_cookie`][cookie] and [`vary_on_headers`][headers].
|
||||||
|
|
||||||
```python
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.decorators import method_decorator
|
from django.views.decorators.cache import cache_page
|
||||||
from django.views.decorators.cache import cache_page
|
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
|
||||||
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
|
|
||||||
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(viewsets.ViewSet):
|
class UserViewSet(viewsets.ViewSet):
|
||||||
# With cookie: cache requested url for each user for 2 hours
|
# With cookie: cache requested url for each user for 2 hours
|
||||||
@method_decorator(cache_page(60*60*2))
|
@method_decorator(cache_page(60*60*2))
|
||||||
@method_decorator(vary_on_cookie)
|
@method_decorator(vary_on_cookie)
|
||||||
def list(self, request, format=None):
|
def list(self, request, format=None):
|
||||||
content = {
|
content = {
|
||||||
'user_feed': request.user.get_user_feed()
|
'user_feed': request.user.get_user_feed()
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
|
||||||
class ProfileView(APIView):
|
class ProfileView(APIView):
|
||||||
# With auth: cache requested url for each user for 2 hours
|
# With auth: cache requested url for each user for 2 hours
|
||||||
@method_decorator(cache_page(60*60*2))
|
@method_decorator(cache_page(60*60*2))
|
||||||
@method_decorator(vary_on_headers("Authorization",))
|
@method_decorator(vary_on_headers("Authorization",))
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
content = {
|
content = {
|
||||||
'user_feed': request.user.get_user_feed()
|
'user_feed': request.user.get_user_feed()
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
|
|
||||||
|
|
||||||
class PostView(APIView):
|
class PostView(APIView):
|
||||||
# Cache page for the requested url
|
# Cache page for the requested url
|
||||||
@method_decorator(cache_page(60*60*2))
|
@method_decorator(cache_page(60*60*2))
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
content = {
|
content = {
|
||||||
'title': 'Post title',
|
'title': 'Post title',
|
||||||
'body': 'Post content'
|
'body': 'Post content'
|
||||||
}
|
}
|
||||||
return Response(content)
|
return Response(content)
|
||||||
```
|
|
||||||
|
|
||||||
**NOTE:** The [`cache_page`][page] decorator only caches the
|
**NOTE:** The [`cache_page`][page] decorator only caches the
|
||||||
`GET` and `HEAD` responses with status 200.
|
`GET` and `HEAD` responses with status 200.
|
||||||
|
|
|
@ -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:
|
**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}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,7 @@ The following sections explain more.
|
||||||
|
|
||||||
If your schema is static, you can use the `generateschema` management command:
|
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
|
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
|
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`:
|
In `urls.py`:
|
||||||
|
|
||||||
```python
|
from rest_framework.schemas import get_schema_view
|
||||||
from rest_framework.schemas import get_schema_view
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# ...
|
# ...
|
||||||
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
||||||
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
||||||
# * Provide view name for use with `reverse()`.
|
# * Provide view name for use with `reverse()`.
|
||||||
path('openapi', get_schema_view(
|
path('openapi', get_schema_view(
|
||||||
title="Your Project",
|
title="Your Project",
|
||||||
description="API for all things …",
|
description="API for all things …",
|
||||||
version="1.0.0"
|
version="1.0.0"
|
||||||
), name='openapi-schema'),
|
), name='openapi-schema'),
|
||||||
# ...
|
# ...
|
||||||
]
|
]
|
||||||
```
|
|
||||||
|
|
||||||
#### `get_schema_view()`
|
#### `get_schema_view()`
|
||||||
|
|
||||||
|
@ -139,9 +135,7 @@ The `get_schema_view()` helper takes the following keyword arguments:
|
||||||
|
|
||||||
**Schema-level customization**
|
**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
|
`SchemaGenerator` is a class that walks a list of routed URL patterns, requests
|
||||||
the schema for each view and collates the resulting OpenAPI schema.
|
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
|
dictionary For example you might wish to add terms of service to the [top-level
|
||||||
`info` object][info-object]:
|
`info` object][info-object]:
|
||||||
|
|
||||||
```
|
class TOSSchemaGenerator(SchemaGenerator):
|
||||||
class TOSSchemaGenerator(SchemaGenerator):
|
def get_schema(self, *args, **kwargs):
|
||||||
def get_schema(self, *args, **kwargs):
|
schema = super().get_schema(*args, **kwargs)
|
||||||
schema = super().get_schema(*args, **kwargs)
|
schema["info"]["termsOfService"] = "https://example.com/tos.html"
|
||||||
schema["info"]["termsOfService"] = "https://example.com/tos.html"
|
return schema
|
||||||
return schema
|
|
||||||
```
|
|
||||||
|
|
||||||
## AutoSchema
|
## AutoSchema
|
||||||
|
|
||||||
**Per-View Customization**
|
**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
|
By default, view introspection is performed by an `AutoSchema` instance
|
||||||
accessible via the `schema` attribute on `APIView`.
|
accessible via the `schema` attribute on `APIView`.
|
||||||
|
@ -209,10 +199,8 @@ and path:
|
||||||
the endpoint, including path and query parameters for pagination, filtering,
|
the endpoint, including path and query parameters for pagination, filtering,
|
||||||
and so on.
|
and so on.
|
||||||
|
|
||||||
```python
|
components = auto_schema.get_components(...)
|
||||||
components = auto_schema.get_components(...)
|
operation = auto_schema.get_operation(...)
|
||||||
operation = auto_schema.get_operation(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
In compiling the schema, `SchemaGenerator` calls `get_components()` and
|
In compiling the schema, `SchemaGenerator` calls `get_components()` and
|
||||||
`get_operation()` for each view, allowed method, and path.
|
`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
|
views, serializers, or fields when customizing the schema generation. You might
|
||||||
be tempted to do something like this:
|
be tempted to do something like this:
|
||||||
|
|
||||||
```python
|
class CustomSchema(AutoSchema):
|
||||||
class CustomSchema(AutoSchema):
|
"""
|
||||||
"""
|
AutoSchema subclass using schema_extra_info on the view.
|
||||||
AutoSchema subclass using schema_extra_info on the view.
|
"""
|
||||||
"""
|
...
|
||||||
...
|
|
||||||
|
|
||||||
class CustomView(APIView):
|
class CustomView(APIView):
|
||||||
schema = CustomSchema()
|
schema = CustomSchema()
|
||||||
schema_extra_info = ... some extra info ...
|
schema_extra_info = ... some extra info ...
|
||||||
```
|
|
||||||
|
|
||||||
Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
|
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
|
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
|
Instead try to subclass `AutoSchema` such that the `extra_info` doesn't leak
|
||||||
out into the view:
|
out into the view:
|
||||||
|
|
||||||
```python
|
class BaseSchema(AutoSchema):
|
||||||
class BaseSchema(AutoSchema):
|
"""
|
||||||
"""
|
AutoSchema subclass that knows how to use extra_info.
|
||||||
AutoSchema subclass that knows how to use extra_info.
|
"""
|
||||||
"""
|
...
|
||||||
...
|
|
||||||
|
|
||||||
class CustomSchema(BaseSchema):
|
class CustomSchema(BaseSchema):
|
||||||
extra_info = ... some extra info ...
|
extra_info = ... some extra info ...
|
||||||
|
|
||||||
class CustomView(APIView):
|
class CustomView(APIView):
|
||||||
schema = CustomSchema()
|
schema = CustomSchema()
|
||||||
```
|
|
||||||
|
|
||||||
This style is slightly more verbose but maintains the encapsulation of the
|
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
|
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
|
subclass per-view, you may find it more convenient to allow specifying the
|
||||||
option as an `__init__()` kwarg to your base `AutoSchema` subclass:
|
option as an `__init__()` kwarg to your base `AutoSchema` subclass:
|
||||||
|
|
||||||
```python
|
class CustomSchema(BaseSchema):
|
||||||
class CustomSchema(BaseSchema):
|
def __init__(self, **kwargs):
|
||||||
def __init__(self, **kwargs):
|
# store extra_info for later
|
||||||
# store extra_info for later
|
self.extra_info = kwargs.pop("extra_info")
|
||||||
self.extra_info = kwargs.pop("extra_info")
|
super().__init__(**kwargs)
|
||||||
super().__init__(**kwargs)
|
|
||||||
|
|
||||||
class CustomView(APIView):
|
class CustomView(APIView):
|
||||||
schema = CustomSchema(
|
schema = CustomSchema(
|
||||||
extra_info=... some extra info ...
|
extra_info=... some extra info ...
|
||||||
)
|
)
|
||||||
```
|
|
||||||
|
|
||||||
This saves you having to create a custom subclass per-view for a commonly used option.
|
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:
|
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):
|
||||||
class CustomSchema(AutoSchema):
|
"""Extension of ``AutoSchema`` to add support for custom field schemas."""
|
||||||
"""Extension of ``AutoSchema`` to add support for custom field schemas."""
|
|
||||||
|
|
||||||
def map_field(self, field):
|
def map_field(self, field):
|
||||||
# Handle SerializerMethodFields or custom fields here...
|
# Handle SerializerMethodFields or custom fields here...
|
||||||
# ...
|
# ...
|
||||||
return super().map_field(field)
|
return super().map_field(field)
|
||||||
```
|
|
||||||
|
|
||||||
Authors of third-party packages should aim to provide an `AutoSchema` subclass,
|
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
|
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:
|
You pass the kwargs when declaring the `AutoSchema` instance on your view:
|
||||||
|
|
||||||
```
|
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
|
schema = AutoSchema(
|
||||||
schema = AutoSchema(
|
tags=['Pets'],
|
||||||
tags=['Pets'],
|
component_name='Pet',
|
||||||
component_name='Pet',
|
operation_id_base='Pet',
|
||||||
operation_id_base='Pet',
|
)
|
||||||
)
|
...
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Assuming a `Pet` model and `PetSerializer` serializer, the kwargs in this
|
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
|
example are probably not needed. Often, though, you'll need to pass the kwargs
|
||||||
|
|
|
@ -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:
|
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'])
|
@action(detail=True, methods=['post', 'delete'])
|
||||||
def unset_password(self, request, pk=None):
|
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`...:
|
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])
|
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
|
||||||
def set_password(self, request, pk=None):
|
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.
|
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.
|
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')
|
@action(detail=True, methods=['put'], name='Change Password')
|
||||||
def password(self, request, pk=None):
|
def password(self, request, pk=None):
|
||||||
"""Update the user's password."""
|
"""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):
|
def delete_password(self, request, pk=None):
|
||||||
"""Delete the user's password."""
|
"""Delete the user's password."""
|
||||||
...
|
...
|
||||||
```
|
|
||||||
|
|
||||||
## Reversing action URLs
|
## 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:
|
Using the example from the previous section:
|
||||||
|
|
||||||
```python
|
>>> view.reverse_action('set-password', args=['1'])
|
||||||
>>> view.reverse_action('set-password', args=['1'])
|
'http://localhost:8000/api/users/1/set_password'
|
||||||
'http://localhost:8000/api/users/1/set_password'
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
|
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
|
||||||
|
|
||||||
```python
|
>>> view.reverse_action(view.set_password.url_name, args=['1'])
|
||||||
>>> view.reverse_action(view.set_password.url_name, args=['1'])
|
'http://localhost:8000/api/users/1/set_password'
|
||||||
'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`.
|
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`.
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,10 @@ update your REST framework settings to include `DEFAULT_SCHEMA_CLASS` explicitly
|
||||||
|
|
||||||
**settings.py**:
|
**settings.py**:
|
||||||
|
|
||||||
```python
|
REST_FRAMEWORK = {
|
||||||
REST_FRAMEWORK = {
|
...
|
||||||
...
|
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
||||||
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
}
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll still be able to keep using CoreAPI schemas, API docs, and client for the
|
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
|
foreseeable future. We'll aim to ensure that the CoreAPI schema generator remains
|
||||||
|
@ -66,21 +64,19 @@ shortcut.
|
||||||
|
|
||||||
In your `urls.py`:
|
In your `urls.py`:
|
||||||
|
|
||||||
```python
|
from rest_framework.schemas import get_schema_view
|
||||||
from rest_framework.schemas import get_schema_view
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# ...
|
# ...
|
||||||
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
# Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
|
||||||
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
# * `title` and `description` parameters are passed to `SchemaGenerator`.
|
||||||
# * Provide view name for use with `reverse()`.
|
# * Provide view name for use with `reverse()`.
|
||||||
path('openapi', get_schema_view(
|
path('openapi', get_schema_view(
|
||||||
title="Your Project",
|
title="Your Project",
|
||||||
description="API for all things …"
|
description="API for all things …"
|
||||||
), name='openapi-schema'),
|
), name='openapi-schema'),
|
||||||
# ...
|
# ...
|
||||||
]
|
]
|
||||||
```
|
|
||||||
|
|
||||||
### Customization
|
### Customization
|
||||||
|
|
||||||
|
|
|
@ -41,20 +41,18 @@ include:
|
||||||
In this example view operation descriptions for the `get` and `post` methods will
|
In this example view operation descriptions for the `get` and `post` methods will
|
||||||
be extracted from the class docstring:
|
be extracted from the class docstring:
|
||||||
|
|
||||||
```python
|
class DocStringExampleListView(APIView):
|
||||||
class DocStringExampleListView(APIView):
|
"""
|
||||||
"""
|
get: A description of my GET operation.
|
||||||
get: A description of my GET operation.
|
post: A description of my POST operation.
|
||||||
post: A description of my POST operation.
|
"""
|
||||||
"""
|
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
|
||||||
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
|
## Validator / Default Context
|
||||||
|
|
||||||
|
@ -71,23 +69,19 @@ The `__call__` method should then include an additional `serializer_field` argum
|
||||||
|
|
||||||
Validator implementations will look like this:
|
Validator implementations will look like this:
|
||||||
|
|
||||||
```python
|
class CustomValidator:
|
||||||
class CustomValidator:
|
requires_context = True
|
||||||
requires_context = True
|
|
||||||
|
|
||||||
def __call__(self, value, serializer_field):
|
def __call__(self, value, serializer_field):
|
||||||
...
|
...
|
||||||
```
|
|
||||||
|
|
||||||
Default implementations will look like this:
|
Default implementations will look like this:
|
||||||
|
|
||||||
```python
|
class CustomDefault:
|
||||||
class CustomDefault:
|
requires_context = True
|
||||||
requires_context = True
|
|
||||||
|
|
||||||
def __call__(self, serializer_field):
|
def __call__(self, serializer_field):
|
||||||
...
|
...
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,9 @@ Method | Path | Tags
|
||||||
|
|
||||||
The tags used for a particular view may also be overridden...
|
The tags used for a particular view may also be overridden...
|
||||||
|
|
||||||
```python
|
class MyOrders(APIView):
|
||||||
class MyOrders(APIView):
|
schema = AutoSchema(tags=['users', 'orders'])
|
||||||
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.
|
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
|
may be overridden if needed](https://www.django-rest-framework.org/api-guide/schemas/#components
|
||||||
)...
|
)...
|
||||||
|
|
||||||
```python
|
class MyOrders(APIView):
|
||||||
class MyOrders(APIView):
|
schema = AutoSchema(component_name="OrderDetails")
|
||||||
schema = AutoSchema(component_name="OrderDetails")
|
|
||||||
```
|
|
||||||
|
|
||||||
## More Public API
|
## 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
|
the double underscore notation for traversing which element of the field the
|
||||||
search should apply to.
|
search should apply to.
|
||||||
|
|
||||||
```python
|
class SitesSearchView(generics.ListAPIView):
|
||||||
class SitesSearchView(generics.ListAPIView):
|
"""
|
||||||
"""
|
An API view to return a list of archaeological sites, optionally filtered
|
||||||
An API view to return a list of archaeological sites, optionally filtered
|
by a search against the site name or location. (Location searches are
|
||||||
by a search against the site name or location. (Location searches are
|
matched against the region and country names.)
|
||||||
matched against the region and country names.)
|
"""
|
||||||
"""
|
queryset = Sites.objects.all()
|
||||||
queryset = Sites.objects.all()
|
serializer_class = SitesSerializer
|
||||||
serializer_class = SitesSerializer
|
filter_backends = [filters.SearchFilter]
|
||||||
filter_backends = [filters.SearchFilter]
|
search_fields = ['site_name', 'location__region', 'location__country']
|
||||||
search_fields = ['site_name', 'location__region', 'location__country']
|
|
||||||
```
|
|
||||||
|
|
||||||
### Searches against annotate fields
|
### Searches against annotate fields
|
||||||
|
|
||||||
Django allows querysets to create additional virtual fields, using the `.annotate`
|
Django allows querysets to create additional virtual fields, using the `.annotate`
|
||||||
method. We now support searching against annotate fields.
|
method. We now support searching against annotate fields.
|
||||||
|
|
||||||
```python
|
class PublisherSearchView(generics.ListAPIView):
|
||||||
class PublisherSearchView(generics.ListAPIView):
|
"""
|
||||||
"""
|
Search for publishers, optionally filtering the search against the average
|
||||||
Search for publishers, optionally filtering the search against the average
|
rating of all their books.
|
||||||
rating of all their books.
|
"""
|
||||||
"""
|
queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
|
||||||
queryset = Publisher.objects.annotate(avg_rating=Avg('book__rating'))
|
serializer_class = PublisherSerializer
|
||||||
serializer_class = PublisherSerializer
|
filter_backends = [filters.SearchFilter]
|
||||||
filter_backends = [filters.SearchFilter]
|
search_fields = ['avg_rating']
|
||||||
search_fields = ['avg_rating']
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -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
|
The most feasible cases where users might be accidentally omitting the keyword arguments
|
||||||
are likely in the composite fields, `ListField` and `DictField`. For instance...
|
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...
|
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.
|
This change has been made because using positional arguments here *does not* result in the expected behaviour.
|
||||||
|
|
||||||
|
|
|
@ -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:
|
* Include the schema view in your URL conf:
|
||||||
|
|
||||||
```py
|
from rest_framework.schemas import get_schema_view
|
||||||
from rest_framework.schemas import get_schema_view
|
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
|
||||||
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
|
|
||||||
|
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
title='Example API',
|
title='Example API',
|
||||||
renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
|
renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('swagger/', schema_view),
|
path('swagger/', schema_view),
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
```
|
|
||||||
|
|
||||||
There have been a large number of fixes to the schema generation. These should
|
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`
|
resolve issues for anyone using the latest version of the `django-rest-swagger`
|
||||||
|
|
|
@ -59,28 +59,24 @@ the schema into your repository.
|
||||||
|
|
||||||
Here's an example of adding an OpenAPI schema to the URL conf:
|
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.schemas import get_schema_view
|
from rest_framework.renderers import JSONOpenAPIRenderer
|
||||||
from rest_framework.renderers import JSONOpenAPIRenderer
|
from django.urls import path
|
||||||
from django.urls import path
|
|
||||||
|
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
title='Server Monitoring API',
|
title='Server Monitoring API',
|
||||||
url='https://www.example.org/api/',
|
url='https://www.example.org/api/',
|
||||||
renderer_classes=[JSONOpenAPIRenderer]
|
renderer_classes=[JSONOpenAPIRenderer]
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('schema.json', schema_view),
|
path('schema.json', schema_view),
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
```
|
|
||||||
|
|
||||||
And here's how you can use the `generateschema` management command:
|
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
|
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/)
|
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:
|
You can use `apistar` to validate your API schema:
|
||||||
|
|
||||||
```shell
|
$ apistar validate --path schema.json --format openapi
|
||||||
$ apistar validate --path schema.json --format openapi
|
✓ Valid OpenAPI schema.
|
||||||
✓ Valid OpenAPI schema.
|
|
||||||
```
|
|
||||||
|
|
||||||
Or to build API documentation:
|
Or to build API documentation:
|
||||||
|
|
||||||
```shell
|
$ apistar docs --path schema.json --format openapi
|
||||||
$ apistar docs --path schema.json --format openapi
|
✓ Documentation built at "build/index.html".
|
||||||
✓ Documentation built at "build/index.html".
|
|
||||||
```
|
|
||||||
|
|
||||||
API Star also includes a [dynamic client library](https://docs.apistar.com/client-library/)
|
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.
|
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...
|
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
|
If you're using custom permission classes then make sure that you are subclassing
|
||||||
from `BasePermission` in order to enable this support.
|
from `BasePermission` in order to enable this support.
|
||||||
|
|
|
@ -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]
|
* 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]
|
* Change `CharField` to disallow null bytes. [#6073][gh6073]
|
||||||
To revert to the old behavior, subclass `CharField` and remove `ProhibitNullCharactersValidator` from the validators.
|
To revert to the old behavior, subclass `CharField` and remove `ProhibitNullCharactersValidator` from the validators.
|
||||||
```python
|
|
||||||
class NullableCharField(serializers.CharField):
|
class NullableCharField(serializers.CharField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
|
self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
|
||||||
```
|
|
||||||
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
|
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
|
||||||
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
|
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
|
||||||
* Allow permissions to be composed [#5753][gh5753]
|
* Allow permissions to be composed [#5753][gh5753]
|
||||||
|
|
|
@ -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
|
a dynamic `SchemaView`, a minimal Django template for using Swagger UI might be
|
||||||
this:
|
this:
|
||||||
|
|
||||||
```html
|
<!DOCTYPE html>
|
||||||
<!DOCTYPE html>
|
<html>
|
||||||
<html>
|
<head>
|
||||||
<head>
|
<title>Swagger</title>
|
||||||
<title>Swagger</title>
|
<meta charset="utf-8"/>
|
||||||
<meta charset="utf-8"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<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" />
|
||||||
<link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
|
</head>
|
||||||
</head>
|
<body>
|
||||||
<body>
|
<div id="swagger-ui"></div>
|
||||||
<div id="swagger-ui"></div>
|
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
|
||||||
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
|
<script>
|
||||||
<script>
|
const ui = SwaggerUIBundle({
|
||||||
const ui = SwaggerUIBundle({
|
url: "{% url schema_url %}",
|
||||||
url: "{% url schema_url %}",
|
dom_id: '#swagger-ui',
|
||||||
dom_id: '#swagger-ui',
|
presets: [
|
||||||
presets: [
|
SwaggerUIBundle.presets.apis,
|
||||||
SwaggerUIBundle.presets.apis,
|
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||||||
SwaggerUIBundle.SwaggerUIStandalonePreset
|
],
|
||||||
],
|
layout: "BaseLayout",
|
||||||
layout: "BaseLayout",
|
requestInterceptor: (request) => {
|
||||||
requestInterceptor: (request) => {
|
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
|
||||||
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
|
return request;
|
||||||
return request;
|
}
|
||||||
}
|
})
|
||||||
})
|
</script>
|
||||||
</script>
|
</body>
|
||||||
</body>
|
</html>
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
Save this in your templates folder as `swagger-ui.html`. Then route a
|
Save this in your templates folder as `swagger-ui.html`. Then route a
|
||||||
`TemplateView` in your project's URL conf:
|
`TemplateView` in your project's URL conf:
|
||||||
|
|
||||||
```python
|
from django.views.generic import TemplateView
|
||||||
from django.views.generic import TemplateView
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# ...
|
# ...
|
||||||
# Route TemplateView to serve Swagger UI template.
|
# Route TemplateView to serve Swagger UI template.
|
||||||
# * Provide `extra_context` with view name of `SchemaView`.
|
# * Provide `extra_context` with view name of `SchemaView`.
|
||||||
path('swagger-ui/', TemplateView.as_view(
|
path('swagger-ui/', TemplateView.as_view(
|
||||||
template_name='swagger-ui.html',
|
template_name='swagger-ui.html',
|
||||||
extra_context={'schema_url':'openapi-schema'}
|
extra_context={'schema_url':'openapi-schema'}
|
||||||
), name='swagger-ui'),
|
), name='swagger-ui'),
|
||||||
]
|
]
|
||||||
```
|
|
||||||
|
|
||||||
See the [Swagger UI documentation][swagger-ui] for advanced usage.
|
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
|
a dynamic `SchemaView`, a minimal Django template for using ReDoc might be
|
||||||
this:
|
this:
|
||||||
|
|
||||||
```html
|
<!DOCTYPE html>
|
||||||
<!DOCTYPE html>
|
<html>
|
||||||
<html>
|
<head>
|
||||||
<head>
|
<title>ReDoc</title>
|
||||||
<title>ReDoc</title>
|
<!-- needed for adaptive design -->
|
||||||
<!-- needed for adaptive design -->
|
<meta charset="utf-8"/>
|
||||||
<meta charset="utf-8"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<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">
|
||||||
<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 -->
|
||||||
<!-- ReDoc doesn't change outer page styles -->
|
<style>
|
||||||
<style>
|
body {
|
||||||
body {
|
margin: 0;
|
||||||
margin: 0;
|
padding: 0;
|
||||||
padding: 0;
|
}
|
||||||
}
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body>
|
||||||
<body>
|
<redoc spec-url='{% url schema_url %}'></redoc>
|
||||||
<redoc spec-url='{% url schema_url %}'></redoc>
|
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
</body>
|
||||||
</body>
|
</html>
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
Save this in your templates folder as `redoc.html`. Then route a `TemplateView`
|
Save this in your templates folder as `redoc.html`. Then route a `TemplateView`
|
||||||
in your project's URL conf:
|
in your project's URL conf:
|
||||||
|
|
||||||
```python
|
from django.views.generic import TemplateView
|
||||||
from django.views.generic import TemplateView
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# ...
|
# ...
|
||||||
# Route TemplateView to serve the ReDoc template.
|
# Route TemplateView to serve the ReDoc template.
|
||||||
# * Provide `extra_context` with view name of `SchemaView`.
|
# * Provide `extra_context` with view name of `SchemaView`.
|
||||||
path('redoc/', TemplateView.as_view(
|
path('redoc/', TemplateView.as_view(
|
||||||
template_name='redoc.html',
|
template_name='redoc.html',
|
||||||
extra_context={'schema_url':'openapi-schema'}
|
extra_context={'schema_url':'openapi-schema'}
|
||||||
), name='redoc'),
|
), name='redoc'),
|
||||||
]
|
]
|
||||||
```
|
|
||||||
|
|
||||||
See the [ReDoc documentation][redoc] for advanced usage.
|
See the [ReDoc documentation][redoc] for advanced usage.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user