Browse Source

Merge 0d42a34562 into 1d434dec47

pull/12504/merge
Nikita Zavadin 4 days ago
committed by GitHub
parent
commit
7011767cea
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      fastapi/_compat.py
  2. 2
      fastapi/utils.py
  3. 45
      tests/test_compat.py

10
fastapi/_compat.py

@ -88,6 +88,7 @@ if PYDANTIC_V2:
class ModelField: class ModelField:
field_info: FieldInfo field_info: FieldInfo
name: str name: str
model_config: Union[BaseConfig, None] = None
mode: Literal["validation", "serialization"] = "validation" mode: Literal["validation", "serialization"] = "validation"
@property @property
@ -109,7 +110,8 @@ if PYDANTIC_V2:
def __post_init__(self) -> None: def __post_init__(self) -> None:
self._type_adapter: TypeAdapter[Any] = TypeAdapter( 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, # type: ignore[arg-type]
) )
def get_default(self) -> Any: def get_default(self) -> Any:
@ -283,7 +285,11 @@ if PYDANTIC_V2:
def get_model_fields(model: Type[BaseModel]) -> List[ModelField]: def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
return [ return [
ModelField(field_info=field_info, name=name) ModelField(
field_info=field_info,
name=name,
model_config=model.model_config, # type: ignore[arg-type]
)
for name, field_info in model.model_fields.items() for name, field_info in model.model_fields.items()
] ]

2
fastapi/utils.py

@ -138,7 +138,7 @@ def create_cloned_field(
new_field.class_validators = field.class_validators # type: ignore[attr-defined] new_field.class_validators = field.class_validators # type: ignore[attr-defined]
new_field.default = field.default # type: ignore[misc] new_field.default = field.default # type: ignore[misc]
new_field.required = field.required # type: ignore[misc] new_field.required = field.required # type: ignore[misc]
new_field.model_config = field.model_config # type: ignore[attr-defined] new_field.model_config = field.model_config
new_field.field_info = field.field_info new_field.field_info = field.field_info
new_field.allow_none = field.allow_none # type: ignore[attr-defined] new_field.allow_none = field.allow_none # type: ignore[attr-defined]
new_field.validate_always = field.validate_always # type: ignore[attr-defined] new_field.validate_always = field.validate_always # type: ignore[attr-defined]

45
tests/test_compat.py

@ -80,6 +80,51 @@ def test_complex():
assert response2.json() == [1, 2] 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(): def test_is_bytes_sequence_annotation_union():
# For coverage # For coverage
# TODO: in theory this would allow declaring types that could be lists of bytes # TODO: in theory this would allow declaring types that could be lists of bytes

Loading…
Cancel
Save