Search code examples
djangodjango-rest-frameworkopenapidrf-spectacular

Define component schema with drf-spectacular for django API


I am using drf-spectacular to generate an OpenAPI schema for django. As I am not using serializers, I am defining everything in the extend_schema decorator. Now my question is, if it is possible to manually define a component schema.

Here is an example what my api view looks like:

from rest_framework.decorators import api_view
from drf_spectacular.utils import (extend_schema, OpenApiExample)
from drf_spectacular.types import OpenApiTypes
from rest_framework.response import Response

@extend_schema(
    examples=[OpenApiExample(
        value=[
            {'title': 'A title'},
            {'title': 'Another title'},
        ],
    )],
    responses={
       200: OpenApiTypes.OBJECT
    }
)
@api_view(['GET'])
def list_articles(request):
    return Response([{'title': 'Test1'}, {'title': 'Test2'}])

and the corresponding component is shown as empty (e.g. in swagger):

swagger-ui example of empty component

Here is the definition in the documentation but I cannot figure out how to achieve it with drf-spectacular.


Solution

  • OpenApiTypes.OBJECT means that the response object can have any amount of fields. Of course Swagger UI cannot know those fields in advance and thus it shows {}, which may be unintuitive but it is correct.

    What you want is to give your response some structure. Everything in spectacular revolves around serializers/components. If you don't want to have explicit serializers, you can create implicit ones with inline_serializer

    from drf_spectacular.utils import extend_schema, OpenApiExample, inline_serializer
    
    @extend_schema(
        examples=[OpenApiExample(
            value=[
                {'title': 'A title'},
                {'title': 'Another title'},
            ],
        )],
        responses={
           200: inline_serializer(
               name='Article',
               fields={
                   'title': serializers.CharField(),
               }
           )
        }
    )
    @api_view(['GET'])
    def list_articles(request):
        pass