Custom schema generation¶
If the default spec generation does not quite match what you were hoping to achieve, drf-yasg
provides some
custom behavior hooks by default.
Swagger spec overview¶
This library generates OpenAPI 2.0 documents. The authoritative specification for this document’s structure will always be the official documentation over at swagger.io and the OpenAPI 2.0 specification page.
Beause the above specifications are a bit heavy and convoluted, here is a general overview of how the specification
is structured, starting from the root Swagger
object.
Operation
contains the following information about each operation:operationId
- should be unique across all operationstags
- used to group operations in the listing
It is interesting to note the main differences between Parameter
and Schema
objects:
Schema |
Parameter |
---|---|
Can nest other Schemas | Cannot nest other Parameters Can only nest a Schema if the parameter is in: body |
Cannot describe file uploads - file is not permitted as a value for type |
Can describe file uploads via type = file , but only as part of a form Operation [1] |
Can be used in Response s |
Cannot be used in Response s |
Cannot be used in form Operation s [1] |
Can be used in form Operation s [1] |
Can only describe request or response bodies | Can describe query , form , header or path
parameters |
[1] | (1, 2, 3) a form Operation is an
|
Default behavior¶
This section describes where information is sourced from when using the default generation process.
Paths
are generated by exploring the patterns registered in your defaulturlconf
, or thepatterns
andurlconf
you specified when constructingOpenAPISchemaGenerator
; only views inheriting from Django Rest Framework’sAPIView
are looked at, all other views are ignoredpath
Parameter
s are generated by looking in the URL pattern for any template parameters; attempts are made to guess their type from the viewsqueryset
andlookup_field
, if applicable. You can override path parameters viamanual_parameters
in @swagger_auto_schema.query
Parameter
s - i.e. parameters specified in the URL as/path/?query1=value&query2=value
- are generated from your view’sfilter_backends
andpaginator
, if any are declared. Additional parameters can be specified via thequery_serializer
andmanual_parameters
arguments of @swagger_auto_schemaThe request body is only generated for the HTTP
POST
,PUT
andPATCH
methods, and is sourced from the view’sserializer_class
. You can also override the request body using therequest_body
argument of @swagger_auto_schema.- if the view represents a form request (that is, all its parsers are of the
multipart/form-data
orapplication/x-www-form-urlencoded
media types), the request body will be output asform
Parameter
s - if it is not a form request, the request body will be output as a single
body
Parameter
wrapped around aSchema
- if the view represents a form request (that is, all its parsers are of the
header
Parameter
s are supported by the OpenAPI specification but are never generated by this library; you can still add them usingmanual_parameters
.Responses
are generated as follows:if
responses
is provided to @swagger_auto_schema and contains at least one success status code (i.e. any 2xx status code), no automatic response is generated and the given response is used as described in the@swagger_auto_schema documentation
otherwise, an attempt is made to generate a default response:
- the success status code is assumed to be
204` for ``DELETE
requests,201
forPOST
requests, and200
for all other request methods - if the view has a request body, the same
Serializer
orSchema
as in the request body is used in generating theResponse
schema; this is inline with the defaultGenericAPIView
andGenericViewSet
behavior - if the view has no request body, its
serializer_class
is used to generate theResponse
schema - if the view is a list view (as defined by
is_list_view()
), the response schema is wrapped in an array - if the view is also paginated, the response schema is then wrapped in the appropriate paging response structure
- the description of the response is left blank
- the success status code is assumed to be
Response
headers are supported by the OpenAPI specification but not currently supported by this library; you can still add them manually by providing an appropriately structured dictionary to theheaders
property of aResponse
objectdescriptions for
Operation
s,Parameter
s andSchema
s are picked up from docstrings andhelp_text
attributes in the same manner as the default DRF SchemaGenerator
The @swagger_auto_schema
decorator¶
You can use the @swagger_auto_schema
decorator on view functions to override
some properties of the generated Operation
. For example, in a ViewSet
,
@swagger_auto_schema(operation_description="partial_update description override", responses={404: 'slug not found'})
def partial_update(self, request, *args, **kwargs):
"""partial_update method docstring"""
...
will override the description of the PATCH /article/{id}/
operation, and document a 404 response with no body and
the given description.
Where you can use the @swagger_auto_schema
decorator depends on the type of your view:
for function based
@api_view
s, because the same view can handle multiple methods, and thus represent multiple operations, you have to add the decorator multiple times if you want to override different operations:test_param = openapi.Parameter('test', openapi.IN_QUERY, description="test manual param", type=openapi.TYPE_BOOLEAN) user_response = openapi.Response('response description', UserSerializer) # 'method' can be used to customize a single HTTP method of a view @swagger_auto_schema(method='get', manual_parameters=[test_param], responses={200: user_response}) # 'methods' can be used to apply the same modification to multiple methods @swagger_auto_schema(methods=['put', 'post'], request_body=UserSerializer) @api_view(['GET', 'PUT', 'POST']) def user_detail(request, pk): ...
for class based
APIView
,GenericAPIView
and non-ViewSet
derivatives, you have to decorate the respective method of each operation:class UserList(APIView): @swagger_auto_schema(responses={200: UserSerializer(many=True)}) def get(self, request): ... @swagger_auto_schema(operation_description="description") def post(self, request): ...
for
ViewSet
,GenericViewSet
,ModelViewSet
, because each viewset corresponds to multiple paths, you have to decorate the action methods, i.e.list
,create
,retrieve
, etc.
Additionally,@list_route
s or@detail_route
s defined on the viewset, like function based api views, can respond to multiple HTTP methods and thus have multiple operations that must be decorated separately:class ArticleViewSet(viewsets.ModelViewSet): # method or 'methods' can be skipped because the list_route only handles a single method (GET) @swagger_auto_schema(operation_description='GET /articles/today/') @list_route(methods=['get']) def today(self, request): ... @swagger_auto_schema(method='get', operation_description="GET /articles/{id}/image/") @swagger_auto_schema(method='post', operation_description="POST /articles/{id}/image/") @detail_route(methods=['get', 'post'], parser_classes=(MultiPartParser,)) def image(self, request, id=None): ... @swagger_auto_schema(operation_description="PUT /articles/{id}/") def update(self, request, *args, **kwargs): ... @swagger_auto_schema(operation_description="PATCH /articles/{id}/") def partial_update(self, request, *args, **kwargs): ...
Tip
If you want to customize the generation of a method you are not implementing yourself, you can use
swagger_auto_schema
in combination with Django’s method_decorator
:
@method_decorator(name='list', decorator=swagger_auto_schema(
operation_description="description from swagger_auto_schema via method_decorator"
))
class ArticleViewSet(viewsets.ModelViewSet):
...
This allows you to avoid unnecessarily overriding the method.
Tip
You can go even further and directly decorate the result of as_view
, in the same manner you would
override an @api_view
as described above:
decorated_login_view = \
swagger_auto_schema(
method='post',
responses={status.HTTP_200_OK: LoginResponseSerializer}
)(LoginView.as_view())
urlpatterns = [
...
url(r'^login/$', decorated_login_view, name='login')
]
This can allow you to avoid skipping an unnecessary subclass altogether.
Warning
However, do note that both of the methods above can lead to unexpected (and maybe surprising) results by replacing/decorating methods on the base class itself.
Subclassing and extending¶
For more advanced control you can subclass SwaggerAutoSchema
- see the documentation page for a list of
methods you can override.
You can put your custom subclass to use by setting it on a view method using the
@swagger_auto_schema
decorator described above.
If you need to control things at a higher level than Operation
objects (e.g. overall document structure,
vendor extensions in metadata) you can also subclass OpenAPISchemaGenerator
- again, see the documentation
page for a list of its methods.
This custom generator can be put to use by setting it as the generator_class
of a SchemaView
using
get_schema_view()
.