From 905dfe90e44ac689527629e3d138615870935e86 Mon Sep 17 00:00:00 2001 From: Nikita Zavadin Date: Mon, 21 Oct 2024 18:31:13 +0200 Subject: [PATCH] fix: Propagate Pydantic's model config --- fastapi/_compat.py | 8 ++++++-- tests/test_compat.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/fastapi/_compat.py b/fastapi/_compat.py index 56c5d744e..9934288be 100644 --- a/fastapi/_compat.py +++ b/fastapi/_compat.py @@ -87,6 +87,7 @@ if PYDANTIC_V2: class ModelField: field_info: FieldInfo name: str + model_config: BaseConfig | None = None mode: Literal["validation", "serialization"] = "validation" @property @@ -108,7 +109,8 @@ if PYDANTIC_V2: def __post_init__(self) -> None: self._type_adapter: TypeAdapter[Any] = TypeAdapter( - Annotated[self.field_info.annotation, self.field_info] + Annotated[self.field_info.annotation, self.field_info], + config=self.model_config, ) def get_default(self) -> Any: @@ -282,7 +284,9 @@ if PYDANTIC_V2: def get_model_fields(model: Type[BaseModel]) -> List[ModelField]: return [ - ModelField(field_info=field_info, name=name) + ModelField( + field_info=field_info, name=name, model_config=model.model_config + ) for name, field_info in model.model_fields.items() ] diff --git a/tests/test_compat.py b/tests/test_compat.py index f4a3093c5..bca1598a4 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -80,6 +80,34 @@ def test_complex(): assert response2.json() == [1, 2] +def test_propagates_pydantic_model_config(): + app = FastAPI() + + class Missing: + def __bool__(self): + return False + + class Model(BaseModel): + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) + value: str | Missing = Missing() + + @app.post("/") + def foo(req: Model) -> str | None: + return req.value or None + + client = TestClient(app) + + response = client.post("/", json={}) + assert response.status_code == 200, response.text + assert response.json() is None + + response2 = client.post("/", json={"value": "foo"}) + assert response2.status_code == 200, response2.text + assert response2.json() == "foo" + + def test_is_bytes_sequence_annotation_union(): # For coverage # TODO: in theory this would allow declaring types that could be lists of bytes