resolved RESOLVED UNSUBMITTED PASS · None tool calls · 0 s · fastapi/fastapi
🐛 Fix tagged discriminated union not recognized as body field
## Problem
When using a union type models with a tagged discriminator as an endpoint parameter, like the following:
```py
class Pie(BaseModel):
time_to_cook: int
num_ingredients: int
class ApplePie(Pie):
fruit: Literal["apple"] = "apple"
class PumpkinPie(Pie):
filling: Literal["pumpkin"] = "pumpkin"
def get_discriminator_value(v: Any) -> str:
if isinstance(v, dict):
return v.get("fruit", v.get("filling"))
return getattr(v, "fruit", getattr(v, "filling", None))
Dessert = Annotated[
Annotated[ApplePie, Tag("apple")] | Annotated[PumpkinPie, Tag("pumpkin")],
Discriminator(get_discriminator_value),
]
app = FastAPI()
@app.post("/dessert")
async def dinner(dessert: Dessert) -> dict[str, Any]:
return dessert.model_dump()
```
FastAPI fails to identify it correctly as **body** payload. It incorrectly considers it's a query parameter, which then causes a validation error. The workaround currently is to explicitly tell FastAPI it's a body parameter using `dessert: Annotated[Dessert, Body()]`
## Fix
The solution is to improve the function handling type annotations by handling the case where we can have `Annotated`
types inside an `Annotated` type.
---
Discussed in https://github.com/fastapi/fastapi/discussions/12941| # | Tool | Arguments | Result |
|---|---|---|---|
| No trace captured. | |||
--- a/fastapi/_compat.py
+++ b/fastapi/_compat.py
@@ -590,6 +590,9 @@ def field_annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
if origin is Union or origin is UnionType:
return any(field_annotation_is_complex(arg) for arg in get_args(annotation))
+ if origin is Annotated:
+ return field_annotation_is_complex(get_args(annotation)[0])
+
return (
_annotation_is_complex(annotation)
or _annotation_is_complex(origin). [100%]
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.48.0-py3-none-any/starlette/testclient.py:51
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.48.0-py3-none-any/starlette/testclient.py:51: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
_PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
1 passed, 1 warning in 0.42s