From d68c066246c9a70f179ca165614dac19b8c8beeb Mon Sep 17 00:00:00 2001 From: ad hoc Date: Tue, 2 Dec 2025 05:39:55 +0100 Subject: [PATCH] =?utf8?q?=F0=9F=90=9B=20Fix=20support=20for=20form=20valu?= =?utf8?q?es=20with=20empty=20strings=20interpreted=20as=20missing=20(`Non?= =?utf8?q?e`=20if=20that's=20the=20default),=20for=20compatibility=20with?= =?utf8?q?=20HTML=20forms=20(#13537)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Co-authored-by: Yurii Motov Co-authored-by: Sebastián Ramírez --- fastapi/dependencies/utils.py | 3 ++- tests/test_form_default.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/test_form_default.py diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index d43fa8a516..0f25a3c360 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -902,8 +902,9 @@ async def _extract_form_body( value = serialize_sequence_value(field=field, value=results) if value is not None: values[field.alias] = value + field_aliases = {field.alias for field in body_fields} for key, value in received_body.items(): - if key not in values: + if key not in field_aliases: values[key] = value return values diff --git a/tests/test_form_default.py b/tests/test_form_default.py new file mode 100644 index 0000000000..2a12049d1a --- /dev/null +++ b/tests/test_form_default.py @@ -0,0 +1,35 @@ +from typing import Optional + +from fastapi import FastAPI, File, Form +from starlette.testclient import TestClient +from typing_extensions import Annotated + +app = FastAPI() + + +@app.post("/urlencoded") +async def post_url_encoded(age: Annotated[Optional[int], Form()] = None): + return age + + +@app.post("/multipart") +async def post_multi_part( + age: Annotated[Optional[int], Form()] = None, + file: Annotated[Optional[bytes], File()] = None, +): + return {"file": file, "age": age} + + +client = TestClient(app) + + +def test_form_default_url_encoded(): + response = client.post("/urlencoded", data={"age": ""}) + assert response.status_code == 200 + assert response.text == "null" + + +def test_form_default_multi_part(): + response = client.post("/multipart", data={"age": ""}) + assert response.status_code == 200 + assert response.json() == {"file": None, "age": None} -- 2.47.3