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]
[1](1, 2, 3)

a form Operation is an Operation that consumes multipart/form-data or application/x-www-form-urlencoded

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

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)
    
    @swagger_auto_schema(method='get', manual_parameters=[test_param], responses={200: user_response})
    @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):
       @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):
          ...
    

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