mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
fix: keep old behaviour of json() by default (#3542)
* fix: handle basemodel fallback for custom encoders * put back old behaviour and add to_dict * typo Co-authored-by: Christian Bundy <christianbundy@fraction.io> Co-authored-by: Christian Bundy <christianbundy@fraction.io>
This commit is contained in:
@@ -31,4 +31,4 @@ wolfgang = User(
|
||||
User(name='John', address=Address(city='London', country='UK')),
|
||||
],
|
||||
)
|
||||
print(wolfgang.json())
|
||||
print(wolfgang.json(models_as_dict=False))
|
||||
|
||||
@@ -109,6 +109,9 @@ _(This script is complete, it should run "as is")_
|
||||
|
||||
### Serialising self-reference or other models
|
||||
|
||||
By default, models are serialised as dictionaries.
|
||||
If you want to serialise them differently, you can add `models_as_dict=False` when calling `json()` method
|
||||
and add the classes of the model in `json_encoders`.
|
||||
In case of forward references, you can use a string with the class name instead of the class itself
|
||||
```py
|
||||
{!.tmp_examples/exporting_models_json_forward_ref.py!}
|
||||
|
||||
+4
-2
@@ -454,6 +454,7 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
|
||||
exclude_defaults: bool = False,
|
||||
exclude_none: bool = False,
|
||||
encoder: Optional[Callable[[Any], Any]] = None,
|
||||
models_as_dict: bool = True,
|
||||
**dumps_kwargs: Any,
|
||||
) -> str:
|
||||
"""
|
||||
@@ -469,11 +470,12 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
|
||||
exclude_unset = skip_defaults
|
||||
encoder = cast(Callable[[Any], Any], encoder or self.__json_encoder__)
|
||||
|
||||
# We don't directly call `self.dict()`, which does exactly the same thing but
|
||||
# with `to_dict = True` because we want to keep raw `BaseModel` instances and not as `dict`.
|
||||
# We don't directly call `self.dict()`, which does exactly this with `to_dict=True`
|
||||
# because we want to be able to keep raw `BaseModel` instances and not as `dict`.
|
||||
# This allows users to write custom JSON encoders for given `BaseModel` classes.
|
||||
data = dict(
|
||||
self._iter(
|
||||
to_dict=models_as_dict,
|
||||
by_alias=by_alias,
|
||||
include=include,
|
||||
exclude=exclude,
|
||||
|
||||
+54
-4
@@ -281,7 +281,7 @@ def test_custom_decode_encode():
|
||||
assert m.json() == '{\n "a": 1,\n "b": "foo"\n}'
|
||||
|
||||
|
||||
def test_json_nested_encode():
|
||||
def test_json_nested_encode_models():
|
||||
class Phone(BaseModel):
|
||||
manufacturer: str
|
||||
number: int
|
||||
@@ -314,8 +314,58 @@ def test_json_nested_encode():
|
||||
|
||||
timon.friend = pumbaa
|
||||
|
||||
assert iphone.json() == '{"manufacturer": "Apple", "number": 18002752273}'
|
||||
assert iphone.json(models_as_dict=False) == '{"manufacturer": "Apple", "number": 18002752273}'
|
||||
assert (
|
||||
pumbaa.json() == '{"name": "Pumbaa", "SSN": 234, "birthday": 737424000.0, "phone": 18007267864, "friend": null}'
|
||||
pumbaa.json(models_as_dict=False)
|
||||
== '{"name": "Pumbaa", "SSN": 234, "birthday": 737424000.0, "phone": 18007267864, "friend": null}'
|
||||
)
|
||||
assert timon.json() == '{"name": "Timon", "SSN": 123, "birthday": 738892800.0, "phone": 18002752273, "friend": 234}'
|
||||
assert (
|
||||
timon.json(models_as_dict=False)
|
||||
== '{"name": "Timon", "SSN": 123, "birthday": 738892800.0, "phone": 18002752273, "friend": 234}'
|
||||
)
|
||||
|
||||
|
||||
def test_custom_encode_fallback_basemodel():
|
||||
class MyExoticType:
|
||||
pass
|
||||
|
||||
def custom_encoder(o):
|
||||
if isinstance(o, MyExoticType):
|
||||
return 'exo'
|
||||
raise TypeError('not serialisable')
|
||||
|
||||
class Foo(BaseModel):
|
||||
x: MyExoticType
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
class Bar(BaseModel):
|
||||
foo: Foo
|
||||
|
||||
assert Bar(foo=Foo(x=MyExoticType())).json(encoder=custom_encoder) == '{"foo": {"x": "exo"}}'
|
||||
|
||||
|
||||
def test_custom_encode_error():
|
||||
class MyExoticType:
|
||||
pass
|
||||
|
||||
def custom_encoder(o):
|
||||
raise TypeError('not serialisable')
|
||||
|
||||
class Foo(BaseModel):
|
||||
x: MyExoticType
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
with pytest.raises(TypeError, match='not serialisable'):
|
||||
Foo(x=MyExoticType()).json(encoder=custom_encoder)
|
||||
|
||||
|
||||
def test_recursive():
|
||||
class Model(BaseModel):
|
||||
value: Optional[str]
|
||||
nested: Optional[BaseModel]
|
||||
|
||||
assert Model(value=None, nested=Model(value=None)).json(exclude_none=True) == '{"nested": {}}'
|
||||
|
||||
Reference in New Issue
Block a user