[Django]-Drf-spectacular: Add OpenApiResponse to a serializer-less function-based view

-2πŸ‘

βœ…

In the meanwhile, I found out a satisfying answer that also keeps automatic documentation generators happy:

I created a couple serializers:

from rest_framework import serializers


class CallsignSerializer(serializers.Serializer):
    callsign = serializers.CharField(max_length=200)


class PasscodeSerializer(serializers.Serializer):
    passcode = serializers.CharField(max_length=200)

And in the views (notice the comment before the last line):

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny

from drf_spectacular.utils import extend_schema

from aprs.api.serializers import CallsignSerializer, PasscodeSerializer
from aprs.vendor.passcode import passcode_generator


class PasscodeView(APIView):
    """
    View to get the APRS passcode from a callsign
    """

    permission_classes = [
        AllowAny,
    ]

    serializer_class = CallsignSerializer

    @extend_schema(responses=PasscodeSerializer)
    def post(self, request):
        """
        Get the APRS passcode from a callsign
        """
        serializer = CallsignSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        callsign = serializer.validated_data["callsign"]
        passcode = passcode_generator(callsign)

        passcode_serializer = PasscodeSerializer(data={"passcode": passcode})
        passcode_serializer.is_valid(raise_exception=True)

        # The serializer isn't passed here directly because otherwise DRF's browsable API
        # will create a POST form for the PasscodeSerializer, instead of the
        # CallsignSerializer. It is used in the lines above, instead of passing a
        # dictionary here directly as a form of validation.
        return Response(passcode_serializer.validated_data)
πŸ‘€Rui Oliveira

8πŸ‘

If you don’t have a Serializer, you can use inline_serializer:

from drf_spectacular.utils import extend_schema, OpenApiParameter, inline_serializer

# ...

@extend_schema(
    parameters=[
        OpenApiParameter(name="callsign", required=True, type=str),
    ],
    description="Get an APRS-IS passcode for a given callsign",
    responses={
       200: inline_serializer(
           name='PasscodeResponse',
           fields={
               'passcode': serializers.CharField(),
           }
       ), 
       400: OpenApiResponse(description='Missing callsign'),
    }
)
@api_view(["POST"])
@permission_classes([AllowAny])
def get_passcode(request):
    callsign = request.data.get("callsign", None)
    # ...

0πŸ‘

It is possible to set OpenAPI schema without using Serializers and operating only with dicts:

from rest_framework.views import APIView
from drf_spectacular.utils import extend_schema

view_schema = {
    'description': 'Some login view',
    'auth': None,
    'request': {
        "application/json": {
            "description": "User credentials",
            "type": "object",
            "properties": {
                "username": {
                    "type": "string",
                    "minLength": 1
                },
                "password": {
                    "type": "string",
                    "minLength": 1
                }
            },
            "required": [
                "username",
                "password"
            ]
        }
    },
    'responses': {
        (200, "application/json"): {
            "description": "Success",
            "type": "object",
            "properties": {
                "access_token": {
                    "type": "string",
                    "minLength": 1
                },
                "refresh_token": {
                    "type": "string",
                    "minLength": 1
                }
            },
            "required": [
                "access_token",
                "refresh_token"
            ]
        },
        (401, "application/json"): {
            "description": "Login failure",
            "type": "object",
            "properties": {
                "status": {
                    "type": "number"
                },
                "error": {
                    "type": "string"
                }
            },
            "required": [
                "status"
            ]
        }
    }
}

class LoginView(APIView):

    @extend_schema(**view_schema)
    def post(self, request: Request):
        ...
πŸ‘€rzlvmp

Leave a comment