drf-yasg icon indicating copy to clipboard operation
drf-yasg copied to clipboard

[Feature Request] Pydantic Support for Request body, Response & Query Parameters

Open omfd opened this issue 2 years ago • 12 comments

Hi Guys,

Pydantic by default provide support for schema generation from the Data Model. If someone can create a feature branch for support pydantic objects as well for the above mentioned things - it will be awesome !!

currently it works for drf serializers only -

just a request - might prove helpful for a quite a lot of developers !!

Regards

omfd avatar Aug 30 '21 14:08 omfd

@omfd I'm curious if anyone has experimented with making this work, e.g. using some transformer from pydantic's BaseModel.dict() to drf-yasg's Schema?

antonagestam avatar Sep 22 '21 14:09 antonagestam

@omfd could you provide an example interface for how you think registering pydantic objects could look if this feature is built?

JoelLefkowitz avatar Sep 22 '21 16:09 JoelLefkowitz

@JoelLefkowitz I think it would make sense to use the same facilities as are already in place. Allowing pydantic models in the responses and request_body arguments of swagger_auto_schema, e.g. @swagger_auto_schema(request_body=SomeRequest, responses={200: SomeResponse}). I think that would solve 95% of cases :)

antonagestam avatar Sep 24 '21 07:09 antonagestam

@antonagestam exactly! If you have a look at the Fastapi sample I am mentioning below for reference !

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None

@app.post("/items/", response_model=Item)
async def create_item(item: Item):
    return item

The above is a POST request with Body as of type Item and response is also of the same type Item

Below is the screenshot for the swagger generator Fastapi guys have done for themselves where default support is Pydantic Data Models

Screenshot_2021-10-16_14-42-36

If we add support for adding request_body and responses object derived from Pydantic BaseModels it will be pretty good.

Regards

omfd avatar Oct 16 '21 09:10 omfd

@omfd I'm curious if anyone has experimented with making this work, e.g. using some transformer from pydantic's BaseModel.dict() to drf-yasg's Schema?

@antonagestam I tried to extend SwaggerAutoSchema to accomodate support for the pydantic objects.

But the prime issue is the base class has a strict check of object being of type serializer which makes it incompatible

Even if that base method can be released with support of Union[Serializer, BaseModel]

we can customize it and extend it accordingly as per the need but the above support needs to be there !!

Rest pydantic by default have pretty good method to make it work once that strict check and include pydantic object support

omfd avatar Oct 16 '21 09:10 omfd

@omfd

But the prime issue is the base class has a strict check of object being of type serializer which makes it incompatible

Which check is that? Is it in BaseInspector?

antonagestam avatar Oct 18 '21 09:10 antonagestam

    def get_request_body_parameters(self, consumes):
        """Return the request body parameters for this view. |br|
        This is either:

        -  a list with a single object Parameter with a :class:`.Schema` derived from the request serializer
        -  a list of primitive Parameters parsed as form data

        :param list[str] consumes: a list of accepted MIME types as returned by :meth:`.get_consumes`
        :return: a (potentially empty) list of :class:`.Parameter`\\ s either ``in: body`` or ``in: formData``
        :rtype: list[openapi.Parameter]
        """
        serializer = self.get_request_serializer()
        schema = None
        if serializer is None:
            return []

        if isinstance(serializer, openapi.Schema.OR_REF):
            schema = serializer

        if any(is_form_media_type(encoding) for encoding in consumes):
            if schema is not None:
                raise SwaggerGenerationError("form request body cannot be a Schema")
            return self.get_request_form_parameters(serializer)
        else:
            if schema is None:
                schema = self.get_request_body_schema(serializer)
            return [self.make_body_parameter(schema)] if schema is not None else []

If you follow the method self.get_request_serializer() you will land up at the below method where the check is happening!!!

def force_serializer_instance(serializer):
    """Force `serializer` into a ``Serializer`` instance. If it is not a ``Serializer`` class or instance, raises
    an assertion error.

    :param serializer: serializer class or instance
    :type serializer: serializers.BaseSerializer or type[serializers.BaseSerializer]
    :return: serializer instance
    :rtype: serializers.BaseSerializer
    """
    if inspect.isclass(serializer):
        assert issubclass(serializer, serializers.BaseSerializer), "Serializer required, not %s" % serializer.__name__
        return serializer()

    assert isinstance(serializer, serializers.BaseSerializer), \
        "Serializer class or instance required, not %s" % type(serializer).__name__
    return serializer

omfd avatar Oct 19 '21 08:10 omfd

@omfd

But the prime issue is the base class has a strict check of object being of type serializer which makes it incompatible

Which check is that? Is it in BaseInspector?

Please look the above comment ---- the idea was to override the get_request_body_parameters method

@antonagestam

omfd avatar Oct 19 '21 08:10 omfd

@JoelLefkowitz Hi Joel I have added couple of examples in the thread above as well mentioned the code where I am having trouble in extending the SwaggerAutoSchema to accomodate pydantic support for request body and response

omfd avatar Oct 28 '21 15:10 omfd

Has anyone tried using already provided schema() method in pydantic with already provided openapi.Schema class in drf_yasg to generate the docs, something like this --

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


from drf_yasg.openapi import Schema
from drf_yasg.utils import swagger_auto_schema
@swagger_auto_schema(
        responses={
            status.HTTP_200_OK: Schema(**Item.schema())
        }
    )

Utsavan avatar Dec 31 '21 10:12 Utsavan

Has anyone tried using already provided schema() method in pydantic with already provided openapi.Schema class in drf_yasg to generate the docs, something like this --

This does not seem to work with nested models. I am getting

drf_yasg.errors.SwaggerValidationError: spec validation failed: 
{'ssv': '("Unresolvable JSON pointer: \'definitions/SubModel\'")'}

utapyngo avatar Jul 13 '22 12:07 utapyngo

Consider using the approach here (https://github.com/pydantic/pydantic/issues/889#issuecomment-1064688675) to avoid submodels in the JSON schema generated by pydantic.

jclerman avatar Sep 29 '23 03:09 jclerman