tfranzel / drf-spectacular

Sane and flexible OpenAPI 3 schema generation for Django REST framework.
https://drf-spectacular.readthedocs.io
BSD 3-Clause "New" or "Revised" License
2.4k stars 266 forks source link

Use Pydantic BaseModel for QueryParameters doesn't work? #1315

Open golgor opened 1 month ago

golgor commented 1 month ago

Describe the bug I can get both responses and requests to work fine using pydantic by simply using a pydantic BaseModel instead of a rest_framework.serializers.Serializer. However, when I try to do the same for Parameters in a GET-function it doesn't work. I have tried many ways, but either there is no result showing it the Schema generation fails.

I'm currently using:

djangorestframework = "3.15.2"
pydantic = "2.9.2"
drf-spectacular = "0.27.2"
django = "4.2.15"

To Reproduce Working good:

from drf_spectacular.utils import OpenApiResponse, extend_schema
from pydantic import BaseModel, Field
from rest_framework import serializers
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView

class QueryParamsSerializer(serializers.Serializer):
    limit = serializers.IntegerField(default=100)

class QueryParams(BaseModel):
    limit: int = Field(default=100)

class RestFrameWorkAPIView(APIView):
    """Publically available endpoint to get a list of Device Messages."""

    @extend_schema(
        parameters=[
            QueryParamsSerializer,
        ],
        responses={
            200: OpenApiResponse(response=QueryParams, description="OK"),
        },
    )
    def get(self, request: Request) -> Response:
        """Endpoint that returns a list of data messages send by modules."""
        return Response(status=status.HTTP_200_OK)

image

But if I change from QueryParamsSerializer to QueryParams, I first of all get a type error and it is not possible to generate any schema when I try to open up the SpectacularAPIView.as_view() or SpectacularSwaggerView.as_view().

from drf_spectacular.utils import OpenApiResponse, extend_schema
from pydantic import BaseModel, Field
from rest_framework import serializers
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView

class QueryParamsSerializer(serializers.Serializer):
    limit = serializers.IntegerField(default=100)

class QueryParams(BaseModel):
    limit: int = Field(default=100)

class RestFrameWorkAPIView(APIView):
    """Publically available endpoint to get a list of Device Messages."""

    @extend_schema(
        parameters=[
            QueryParamsSerializer,
        ],
        responses={
            200: OpenApiResponse(response=QueryParams, description="OK"),
        },
    )
    def get(self, request: Request) -> Response:
        """Endpoint that returns a list of data messages send by modules."""
        return Response(status=status.HTTP_200_OK)

I can use the same pydantic class as a response serializer, but not for parameters.

If I try to use a POST-function everything works fine as well.

    @extend_schema(
        request=QueryParams,
        responses={
            200: OpenApiResponse(response=QueryParams, description="OK"),
        },
    )
    def post(self, request: Request) -> Response:
        """Endpoint that returns a list of data messages send by modules."""
        return Response(status=status.HTTP_200_OK)

image

I have also tried to search the documentation about this and also for issues here in the Github repo, as well as extensively trying to interrogate various AI resources and searching on Google.

To be honest, I don't know if this is a bug or if I am doing it wrong, but I honestly feel a bit lost here on how to do in that case. Can anyone please advise?

Expected behavior That using a pydantic model for query parameters generate the correct schema and swagger view as using a rest_framework.serializers.Serializer.

tfranzel commented 2 days ago

Hi, serializer explosion was added as a convenience feature. Never tought many people would use it. It was never meant to be used indirectly through serializer extensions for other classes like pydantic. However, it turns out not that much was actually missing to make it work.

Keep in mind this is a shortcut of a shortcut and the schema might not be 100%, so give it a sanity check first.

golgor commented 2 days ago

@tfranzel Awesome, thanks a lot for looking into this.