pythondjangodjango-rest-frameworkdrf-spectacular

drf-spectacular: Add OpenApiResponse to a serializer-less function-based view


So, I'm documenting the following piece of code using drf-spectacular:

from rest_framework import response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework import status

from drf_spectacular.utils import extend_schema, OpenApiParameter

def passcode_generator:
    return 0 # placeholder


@extend_schema(
    parameters=[
        OpenApiParameter(name="callsign", required=True, type=str),
    ],
    description="Get an APRS-IS passcode for a given callsign",
)
@api_view(["POST"])
@permission_classes([AllowAny])
def get_passcode(request):
    callsign = request.data.get("callsign", None)
    if callsign is None:
        raise Response(
            {"error": "Missing callsign"}, status=status.HTTP_400_BAD_REQUEST
        )
    return Response({"passcode": passcode_generator(callsign)})

What I can't understand how to do is how to document the responses. Namely, there is OpenApiResponse in drf_spectacular.utils but the documentation is very slim. How can I document the responses of my API with this system?


Solution

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