5

I use Django, the DRF, drf-yasg and Swagger Codegen to automatically build TypeScript code to access my REST API.

In the Django backend I added a path to be served with the DRF:

rest_router = routers.DefaultRouter()
rest_router.register(r'source/(?P<source_id>[0-9]+)/document', DocumentViewSet)

DocumentViewSet is a DRF ModelViewSet.

As you can see, the source_id parameter is of numeric type. However, the resulting generated API description defines the source_id parameter as type String.

Obviously the numeric regexp in the path setting is not enough so I guess I need some type annotation in the DocumentViewSet class? I tried the following code, but this showed no effect:

@swagger_auto_schema(
    manual_parameters=[
        openapi.Parameter(name="source_id",
            required=True,
            type="integer",
            in_="path",
            description="Source reference",
        ),
    ],
)
class DocumentViewSet(viewsets.ModelViewSet):
    serializer_class = rest_serializers.DocumentSerializer
    queryset = models.Document.objects.all().order_by('id')

How can I tell drf-yasg to set the source_id parameter to type Integer?

romor
  • 1,181
  • 15
  • 22

2 Answers2

0
  1. The standard OpenAPISchemaGenerator on drf-yasg only checks if the path variable name matches with the view queryset model field, and uses the type of the model field if such exists. Otherwise it defaults to string. Your original code should work if the Document model has a numeric field named "source_id". Foreign relations should work as well, but it's very much possible that in that case your parameter name should be the name of the field (source) and not the id reference (source_id).

  2. @swagger_auto_schema should be applied on the view method or methods, not the view class. AFAIK applying it to view class does nothing. See https://drf-yasg.readthedocs.io/en/stable/custom_spec.html#the-swagger-auto-schema-decorator

  3. As a side quest I poked around a bit to see if it's possible to determine the type using pythons built-in typing, and the short answer is yes, but it's a bit messy. Throwing this out in case anyone finds it useful, use it with your own risk.


class CustomSchemaGenerator(OpenAPISchemaGenerator):
    def get_path_parameters(self, path, view_cls):
        parameters = super().get_path_parameters(path, view_cls)
        for p in parameters:
            if p.in_ == openapi.IN_PATH and p.type == openapi.TYPE_STRING:
                p.type = getattr(view_cls, f'path_type_{p.name}', openapi.TYPE_STRING)
        return parameters

In the above generator, we allow drf-yasg to do the initial type determination, but then add an extra step that allows overriding type in the view class.

Example view

class DocumentView(APIView):
    path_type_source_id = openapi.TYPE_INTEGER

Enable the generator with SWAGGER_SETTINGS

SWAGGER_SETTINGS = {
    'DEFAULT_GENERATOR_CLASS': 'path.to.CustomSchemaGenerator',
}
voneiden
  • 457
  • 4
  • 8
  • There is a source foreign key member in the Document model but renaming the parameter still yields a string. – romor Oct 15 '20 at 11:05
  • Is there a way to also decorate view classes? Would this even help here? I guess this could be another SO question... – romor Oct 15 '20 at 11:10
  • Django provides a utility decorator called "method_decorator" for decorating view classes, but do note it requires the method name as an argument. Also I had a typo in my answer, it should have been `path_type_source_id = openapi.TYPE_INTEGER`. Fixed. – voneiden Oct 16 '20 at 12:18
0

This may be something made easier in a newer version of drf_yasf, but I managed to get it working with something similar to the following way:

from django.utils.decorators import method_decorator
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets


params = [
    openapi.Parameter("source_id",
        openapi.IN_PATH,
        description="Source reference",
        type=openapi.TYPE_INTEGER
    )
]

@method_decorator(name="list", decorator=swagger_auto_schema(manual_parameters=params))
@method_decorator(name="create", decorator=swagger_auto_schema(manual_parameters=params))
@method_decorator(name="retrieve", decorator=swagger_auto_schema(manual_parameters=params))
@method_decorator(name="update", decorator=swagger_auto_schema(manual_parameters=params))
@method_decorator(name="partial_update", decorator=swagger_auto_schema(manual_parameters=params))
@method_decorator(name="destroy", decorator=swagger_auto_schema(manual_parameters=params))
class DocumentViewSet(viewsets.ModelViewSet):
    serializer_class = rest_serializers.DocumentSerializer
    queryset = models.Document.objects.all().order_by('id')

I have here decorated all base ViewSet actions but you could decorate only the ones you need, you can also decorate your custom actions (no need to use method_decorator in that case: you can directly decorate the action with @swagger_auto_schema). Note that you could also decorate the base actions with @swagger_auto_schema directly if you were to override them in your class.

titarch
  • 103
  • 8