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):
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