diff --git a/fastapi/encoders.py b/fastapi/encoders.py index 84893dc808..578b6ee4c7 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -224,8 +224,11 @@ def jsonable_encoder( if exclude is not None and not isinstance(exclude, (set, dict)): exclude = set(exclude) # type: ignore[assignment] # ty: ignore[unused-ignore-comment] if isinstance(obj, BaseModel): + model_dump_mode: str = "json" + if custom_encoder: + model_dump_mode = "python" obj_dict = obj.model_dump( - mode="json", + mode=model_dump_mode, include=include, exclude=exclude, by_alias=by_alias, @@ -237,6 +240,7 @@ def jsonable_encoder( obj_dict, exclude_none=exclude_none, exclude_defaults=exclude_defaults, + custom_encoder=custom_encoder, sqlalchemy_safe=sqlalchemy_safe, ) if dataclasses.is_dataclass(obj): diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 595202beaf..34c609f2bb 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -225,6 +225,25 @@ def test_custom_encoders(): assert encoded_instance2["dt_field"] == instance["dt_field"].isoformat() +def test_custom_encoders_for_pydantic_model_arbitrary_type(): + class CustomType: + def __init__(self, value: str): + self.value = value + + class ModelWithCustomType(BaseModel): + custom: CustomType + + model_config = {"arbitrary_types_allowed": True} + + instance = ModelWithCustomType(custom=CustomType("encoded")) + + encoded_instance = jsonable_encoder( + instance, + custom_encoder={CustomType: lambda o: {"value": o.value}}, + ) + assert encoded_instance == {"custom": {"value": "encoded"}} + + def test_custom_enum_encoders(): def custom_enum_encoder(v: Enum): return v.value.lower()