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.

Excluding endpoints

You can prevent a view from being included in the Swagger view by setting its class-level swagger_schema attribute to None, or you can prevent an operation from being included by setting its auto_schema override to none in @swagger_auto_schema:

class UserList(APIView):
   swagger_schema = None

   # all methods of the UserList class will be excluded

# only the GET method will be shown in Swagger
@swagger_auto_schema(method='put', auto_schema=None)
@swagger_auto_schema(methods=['get'], ...)
@api_view(['GET', 'PUT'])
def user_detail(request, pk):

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):
       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, @actions, @list_route`s 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/')
       @action(detail=False, 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/")
       @action(detail=True, 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):


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.


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 = \
      responses={status.HTTP_200_OK: LoginResponseSerializer}

urlpatterns = [
   url(r'^login/$', decorated_login_view, name='login')

This can allow you to avoid skipping an unnecessary subclass altogether.


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.

Serializer Meta nested class

You can define some per-serializer options by adding a Meta class to your serializer, e.g.:

class WhateverSerializer(Serializer):

   class Meta:
      ... options here ...

The available options are:

  • ref_name - a string which will be used as the model definition name for this serializer class; setting it to None will force the serializer to be generated as an inline model everywhere it is used. If two serializers have the same ref_name, both their usages will be replaced with a reference to the same definition. If this option is not specified, all serializers have an implicit name derived from their class name, minus any Serializer suffix (e.g. UserSerializer -> User, SerializerWithSuffix -> SerializerWithSuffix)
  • swagger_schema_fields - a dictionary mapping Schema field names to values. These attributes will be set on the Schema object generated from the Serializer. Field names must be python values, which are converted to Swagger Schema attribute names according to make_swagger_name(). Attribute names and values must conform to the OpenAPI 2.0 specification.

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, by setting it as a class-level attribute named swagger_schema on the view class, or globally via settings.

For example, to generate all operation IDs as camel case, you could do:

from inflection import camelize

class CamelCaseOperationIDAutoSchema(SwaggerAutoSchema):
   def get_operation_id(self, operation_keys):
      operation_id = super(CamelCaseOperationIDAutoSchema, self).get_operation_id(operation_keys)
      return camelize(operation_id, uppercase_first_letter=False)



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

Inspector classes

For customizing behavior related to specific field, serializer, filter or paginator classes you can implement the FieldInspector, SerializerInspector, FilterInspector, PaginatorInspector classes and use them with @swagger_auto_schema or one of the related settings.

A FilterInspector that adds a description to all DjangoFilterBackend parameters could be implemented like so:

class DjangoFilterDescriptionInspector(CoreAPICompatInspector):
   def get_filter_parameters(self, filter_backend):
      if isinstance(filter_backend, DjangoFilterBackend):
         result = super(DjangoFilterDescriptionInspector, self).get_filter_parameters(filter_backend)
         for param in result:
            if not param.get('description', ''):
               param.description = "Filter the returned list by {field_name}".format(

         return result

      return NotHandled

@method_decorator(name='list', decorator=swagger_auto_schema(
class ArticleViewSet(viewsets.ModelViewSet):
   filter_backends = (DjangoFilterBackend,)
   filter_fields = ('title',)

A second example, of a FieldInspector that removes the title attribute from all generated Schema objects:

class NoSchemaTitleInspector(FieldInspector):
   def process_result(self, result, method_name, obj, **kwargs):
      # remove the `title` attribute of all Schema objects
      if isinstance(result, openapi.Schema.OR_REF):
         # traverse any references and alter the Schema object in place
         schema = openapi.resolve_ref(result, self.components)
         schema.pop('title', None)

         # no ``return schema`` here, because it would mean we always generate
         # an inline `object` instead of a definition reference

      # return back the same object that we got - i.e. a reference if we got a reference
      return result

class NoTitleAutoSchema(SwaggerAutoSchema):
   field_inspectors = [NoSchemaTitleInspector] + swagger_settings.DEFAULT_FIELD_INSPECTORS

class ArticleViewSet(viewsets.ModelViewSet):
   swagger_schema = NoTitleAutoSchema


A note on references - Schema objects are sometimes output by reference (SchemaRef); in fact, that is how named models are implemented in OpenAPI:

  • in the output swagger document there is a definitions section containing Schema objects for all models
  • every usage of a model refers to that single Schema object - for example, in the ArticleViewSet above, all requests and responses containg an Article model would refer to the same schema definition by a '$ref': '#/definitions/Article'

This is implemented by only generating one Schema object for every serializer class encountered.

This means that you should generally avoid view or method-specific FieldInspectors if you are dealing with references (a.k.a named models), because you can never know which view will be the first to generate the schema for a given serializer.

IMPORTANT: nested fields on ModelSerializers that are generated from model ForeignKeys will always be output by value. If you want the by-reference behaviour you have to explictly set the serializer class of nested fields instead of letting ModelSerializer generate one automatically; for example:

class OneSerializer(serializers.ModelSerializer):
   class Meta:
      model = SomeModel
      fields = ('id',)

class AnotherSerializer(serializers.ModelSerializer):
   chilf = OneSerializer()

   class Meta:
      model = SomeParentModel
      fields = ('id', 'child')

Another caveat that stems from this is that any serializer named “NestedSerializer” will be forced inline unless it has a ref_name set explicitly.