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

Currently, the only option you can add here is

  • 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)

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.