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], 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.

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: **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: 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

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: 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`.

View File

@ -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

View File

@ -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):
... ...
```
--- ---

View File

@ -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']
```
--- ---

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 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.

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: * 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`

View File

@ -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.

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] * 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]

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 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.