]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
🐛 Reenable `allow_arbitrary_types` when only 1 argument is used on the API endpoint...
authorrmawatson <rmawatson@hotmail.com>
Sat, 20 Sep 2025 17:25:53 +0000 (19:25 +0200)
committerGitHub <noreply@github.com>
Sat, 20 Sep 2025 17:25:53 +0000 (19:25 +0200)
Co-authored-by: Sofie Van Landeghem <svlandeg@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
fastapi/dependencies/utils.py
tests/test_compat.py

index 1b15e64594508118fdf1a7035c0e5a0ef0cb180d..d9f6bf2d7bea0d4f7a3daeedfd0085fb1fdd62cd 100644 (file)
@@ -922,7 +922,11 @@ async def request_body_to_args(
 
     fields_to_extract: List[ModelField] = body_fields
 
-    if single_not_embedded_field and lenient_issubclass(first_field.type_, BaseModel):
+    if (
+        single_not_embedded_field
+        and lenient_issubclass(first_field.type_, BaseModel)
+        and isinstance(received_body, FormData)
+    ):
         fields_to_extract = get_cached_model_fields(first_field.type_)
 
     if isinstance(received_body, FormData):
index f4a3093c5ee4f7a5ce25d299a318cf4b67c7a297..43c6864896eb5888dee311a94edeaa070477a587 100644 (file)
@@ -80,6 +80,51 @@ def test_complex():
     assert response2.json() == [1, 2]
 
 
+@needs_pydanticv2
+def test_propagates_pydantic2_model_config():
+    app = FastAPI()
+
+    class Missing:
+        def __bool__(self):
+            return False
+
+    class EmbeddedModel(BaseModel):
+        model_config = ConfigDict(arbitrary_types_allowed=True)
+        value: Union[str, Missing] = Missing()
+
+    class Model(BaseModel):
+        model_config = ConfigDict(
+            arbitrary_types_allowed=True,
+        )
+        value: Union[str, Missing] = Missing()
+        embedded_model: EmbeddedModel = EmbeddedModel()
+
+    @app.post("/")
+    def foo(req: Model) -> Dict[str, Union[str, None]]:
+        return {
+            "value": req.value or None,
+            "embedded_value": req.embedded_model.value or None,
+        }
+
+    client = TestClient(app)
+
+    response = client.post("/", json={})
+    assert response.status_code == 200, response.text
+    assert response.json() == {
+        "value": None,
+        "embedded_value": None,
+    }
+
+    response2 = client.post(
+        "/", json={"value": "foo", "embedded_model": {"value": "bar"}}
+    )
+    assert response2.status_code == 200, response2.text
+    assert response2.json() == {
+        "value": "foo",
+        "embedded_value": "bar",
+    }
+
+
 def test_is_bytes_sequence_annotation_union():
     # For coverage
     # TODO: in theory this would allow declaring types that could be lists of bytes