Skip to content

Instantly share code, notes, and snippets.

@chbndrhnns
Last active September 26, 2023 18:49
FastAPI 0.103 loses custom class name
import typing as t
import pytest
from pydantic import BaseModel
from pydantic.json_schema import GenerateJsonSchema
from fastapi import FastAPI
from fastapi._compat import get_definitions
from fastapi.openapi.constants import REF_TEMPLATE
from fastapi.openapi.utils import get_fields_from_routes
T = t.TypeVar("T")
app = FastAPI()
class Response(BaseModel, t.Generic[T]):
data: T
@classmethod
def model_parametrized_name(cls, params: tuple[type[t.Any], ...]) -> str:
return "IntWrapper"
@app.get("/", response_model=Response[int])
async def get():
return {"data": 1}
def test_spec():
spec = app.openapi()
actual = set(spec["components"]["schemas"].keys())
assert "IntWrapper" in actual, actual
def test_internals():
# duplicated from utils.get_openapi()
all_fields = get_fields_from_routes(list(app.routes))
schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
field_mapping, definitions = get_definitions(
fields=all_fields,
schema_generator=schema_generator,
model_name_map={},
)
assert "IntWrapper" in set(definitions), definitions
@chbndrhnns
Copy link
Author

It works with pydantic v1:

import typing as t

from pydantic.generics import GenericModel

from fastapi import FastAPI
from tests.utils import needs_pydanticv1

T = t.TypeVar("T")

app = FastAPI()


class Response(GenericModel, t.Generic[T]):
    data: T

    @classmethod
    def __concrete_name__(cls, params: tuple[type[t.Any], ...]) -> str:
        return "IntWrapper"


@app.get("/", response_model=Response[int])
async def get():
    return {"data": 1}


@needs_pydanticv1
def test_spec():
    spec = app.openapi()
    actual = set(spec["components"]["schemas"].keys())
    assert "IntWrapper" in actual, actual

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment