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.

  • Swagger object
    • info, schemes, securityDefinitions and other informative attributes
    • paths: Paths object
      A list of all the paths in the API in the form of a mapping
      • {path}: PathItem - each PathItem has multiple operations keyed by method
        • {http_method}: Operation
          Each operation is thus uniquely identified by its (path, http_method) combination, e.g. GET /articles/, POST /articles/, etc.
        • parameters: [Parameter] - and a list of path parameters
    • definitions: named Models
      A list of all the named models in the API in the form of a mapping
  • Operation contains the following information about each operation:
    • parameters: [Parameter]
      A list of all the query, header and form parameters accepted by the operation.
      • there can also be at most one body parameter whose structure is represented by a Schema or a reference to one (SchemaRef)
    • responses: Responses
      A list of all the possible responses the operation is expected to return. Each response can optionally have a Schema which describes the structure of its body.
      • {status_code}: Response - mapping of status code to response definition
    • operationId - should be unique across all operations
    • tags - 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 Responses Cannot be used in Responses
Cannot be used in form Operations [1] Can be used in form Operations [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 Operation that consumes multipart/form-data or application/x-www-form-urlencoded content

  • a form Operation cannot have body parameters
  • a non-form operation cannot have form parameters

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 default urlconf, or the patterns and urlconf you specified when constructing OpenAPISchemaGenerator; only views inheriting from Django Rest Framework’s APIView are looked at, all other views are ignored

  • path Parameters are generated by looking in the URL pattern for any template parameters; attempts are made to guess their type from the views queryset and lookup_field, if applicable. You can override path parameters via manual_parameters in @swagger_auto_schema.

  • query Parameters - i.e. parameters specified in the URL as /path/?query1=value&query2=value - are generated from your view’s filter_backends and paginator, if any are declared. Additional parameters can be specified via the query_serializer and manual_parameters arguments of @swagger_auto_schema

  • The request body is only generated for the HTTP POST, PUT and PATCH methods, and is sourced from the view’s serializer_class. You can also override the request body using the request_body argument of @swagger_auto_schema.

    • if the view represents a form request (that is, all its parsers are of the multipart/form-data or application/x-www-form-urlencoded media types), the request body will be output as form Parameters
    • if it is not a form request, the request body will be output as a single body Parameter wrapped around a Schema
  • header Parameters are supported by the OpenAPI specification but are never generated by this library; you can still add them using manual_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 for POST requests, and 200 for all other request methods
      • if the view has a request body, the same Serializer or Schema as in the request body is used in generating the Response schema; this is inline with the default GenericAPIView and GenericViewSet behavior
      • if the view has no request body, its serializer_class is used to generate the Response 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
  • 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 the headers property of a Response object

  • descriptions for Operations, Parameters and Schemas are picked up from docstrings and help_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_views, 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_routes or @detail_routes 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().