From 5c8942bdd478fc3e9e520c250fced907faebec30 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Fri, 12 Aug 2022 11:08:33 +0100 Subject: [PATCH] Revert "Add nested json encoding (#3941)" (#4367) This reverts commit b42fae081ce095769f60cf18d4657ba9b14a97b5. --- changes/3941-lilyminium.md | 1 - .../exporting_models_json_nested_encoders.py | 41 ---------- docs/usage/exporting_models.md | 14 ---- pydantic/main.py | 11 --- tests/test_json.py | 78 ------------------- 5 files changed, 145 deletions(-) delete mode 100644 changes/3941-lilyminium.md delete mode 100644 docs/examples/exporting_models_json_nested_encoders.py diff --git a/changes/3941-lilyminium.md b/changes/3941-lilyminium.md deleted file mode 100644 index 41ed1b935a..0000000000 --- a/changes/3941-lilyminium.md +++ /dev/null @@ -1 +0,0 @@ -add `use_nested_encoders` keyword argument to `BaseModel.json` to allow encoding nested subclasses with encoders specified in the inner classes diff --git a/docs/examples/exporting_models_json_nested_encoders.py b/docs/examples/exporting_models_json_nested_encoders.py deleted file mode 100644 index 557b2a1e33..0000000000 --- a/docs/examples/exporting_models_json_nested_encoders.py +++ /dev/null @@ -1,41 +0,0 @@ -from datetime import datetime, timedelta -from pydantic import BaseModel -from pydantic.json import timedelta_isoformat - - -class CustomChildModel(BaseModel): - dt: datetime - diff: timedelta - - class Config: - json_encoders = { - datetime: lambda v: v.timestamp(), - timedelta: timedelta_isoformat, - } - - -class ParentModel(BaseModel): - diff: timedelta - child: CustomChildModel - - class Config: - json_encoders = { - timedelta: lambda v: v.total_seconds(), - CustomChildModel: lambda _: 'using parent encoder', - } - - -child = CustomChildModel(dt=datetime(2032, 6, 1), diff=timedelta(hours=100)) -parent = ParentModel(diff=timedelta(hours=3), child=child) - -# default encoder uses total_seconds() for diff -print(parent.json()) - -# nested encoder uses isoformat -print(parent.json(use_nested_encoders=True)) - -# turning off models_as_dict only uses the top-level formatter, however - -print(parent.json(models_as_dict=False, use_nested_encoders=True)) - -print(parent.json(models_as_dict=False, use_nested_encoders=False)) diff --git a/docs/usage/exporting_models.md b/docs/usage/exporting_models.md index 27197891e3..62aca5b025 100644 --- a/docs/usage/exporting_models.md +++ b/docs/usage/exporting_models.md @@ -118,20 +118,6 @@ In case of forward references, you can use a string with the class name instead ``` _(This script is complete, it should run "as is")_ -### Nested serialisation of other models - -By default, models that contain other models are serialised using the `json_encoders` functions of the -parent or container class. -However, you may want to nest classes in a modular fashion, including their `json_encoders`. -In this case, call `json(use_nested_encoders=True)`. -`use_nested_encoders` has no effect when `models_as_dict=False`, as the classes of the models -are expected to be defined in the top-level `json_encoders`. - -```py -{!.tmp_examples/exporting_models_json_nested_encoders.py!} -``` -_(This script is complete, it should run "as is")_ - ### Serialising subclasses !!! note diff --git a/pydantic/main.py b/pydantic/main.py index fddacd97a0..faf0cef06a 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -427,7 +427,6 @@ def dict( exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, - encode_as_json: bool = False, ) -> 'DictStrAny': """ Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. @@ -449,7 +448,6 @@ def dict( exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, - encode_as_json=encode_as_json, ) ) @@ -465,7 +463,6 @@ def json( exclude_none: bool = False, encoder: Optional[Callable[[Any], Any]] = None, models_as_dict: bool = True, - use_nested_encoders: bool = False, **dumps_kwargs: Any, ) -> str: """ @@ -493,7 +490,6 @@ def json( exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, - encode_as_json=use_nested_encoders, ) ) if self.__custom_root_type__: @@ -714,7 +710,6 @@ def _get_value( exclude_unset: bool, exclude_defaults: bool, exclude_none: bool, - encode_as_json: bool = False, ) -> Any: if isinstance(v, BaseModel): @@ -726,7 +721,6 @@ def _get_value( include=include, exclude=exclude, exclude_none=exclude_none, - encode_as_json=encode_as_json, ) if ROOT_KEY in v_dict: return v_dict[ROOT_KEY] @@ -776,9 +770,6 @@ def _get_value( elif isinstance(v, Enum) and getattr(cls.Config, 'use_enum_values', False): return v.value - elif encode_as_json: - return cls.__json_encoder__(v) - else: return v @@ -812,7 +803,6 @@ def _iter( exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, - encode_as_json: bool = False, ) -> 'TupleGenerator': # Merge field set excludes with explicit exclude parameter with explicit overriding field set options. @@ -858,7 +848,6 @@ def _iter( exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, - encode_as_json=encode_as_json, ) yield dict_key, v diff --git a/tests/test_json.py b/tests/test_json.py index 17ed7df5e1..1aa210caf6 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -372,81 +372,3 @@ class Model(BaseModel): nested: Optional[BaseModel] assert Model(value=None, nested=Model(value=None)).json(exclude_none=True) == '{"nested": {}}' - - -class WithCustomEncoders(BaseModel): - dt: datetime.datetime - diff: datetime.timedelta - - class Config: - json_encoders = { - datetime.datetime: lambda v: v.timestamp(), - datetime.timedelta: timedelta_isoformat, - } - - -ides_of_march = datetime.datetime(44, 3, 15, tzinfo=datetime.timezone.utc) - -child = WithCustomEncoders( - dt=datetime.datetime(2032, 6, 1, tzinfo=datetime.timezone.utc), - diff=datetime.timedelta(hours=100), -) - - -def test_inner_custom_encoding(): - assert child.json() == r'{"dt": 1969660800.0, "diff": "P4DT4H0M0.000000S"}' - - -def test_encoding_in_parent_with_variable_encoders(): - class ParentWithVariableEncoders(BaseModel): - dt: datetime.datetime - child: WithCustomEncoders - - class Config: - json_encoders = { - datetime.datetime: lambda v: v.year, - datetime.timedelta: lambda v: v.total_seconds(), - } - - parent = ParentWithVariableEncoders(child=child, dt=ides_of_march) - - default = r'{"dt": 44, "child": {"dt": 2032, "diff": 360000.0}}' - assert parent.json() == default - # turning off models_as_dict defaults to top-level - assert parent.json(models_as_dict=False, use_nested_encoders=False) == default - assert parent.json(models_as_dict=False, use_nested_encoders=True) == default - - custom = ( - r'{"dt": 44, ' # parent.dt still uses the year to encode - # child uses child.json_encoders to encode - r'"child": {"dt": 1969660800.0, "diff": "P4DT4H0M0.000000S"}}' - ) - assert parent.json(use_nested_encoders=True) == custom - - -def test_encoding_in_parent_with_class_encoders(): - class ParentWithClassEncoders(BaseModel): - dt: datetime.datetime - child: WithCustomEncoders - - class Config: - json_encoders = { - datetime.datetime: lambda v: v.timestamp(), - WithCustomEncoders: lambda v: {'dt': v.dt.year}, - } - - parent = ParentWithClassEncoders(child=child, dt=ides_of_march) - - # when models_as_dict=True, the `WithCustomEncoders` encoder is ignored - default = r'{"dt": -60772291200.0, "child": {"dt": 1969660800.0, "diff": 360000.0}}' - assert parent.json() == default - - custom_child = r'{"dt": -60772291200.0, "child": {"dt": 1969660800.0, "diff": "P4DT4H0M0.000000S"}}' - assert parent.json(use_nested_encoders=True) == custom_child - - # when models_as_dict=False, the parent `WithCustomEncoders` is used - # regardless of whatever json_encoders are in WithCustomEncoders.Config - - custom_parent = r'{"dt": -60772291200.0, "child": {"dt": 2032}}' - assert parent.json(models_as_dict=False, use_nested_encoders=False) == custom_parent - assert parent.json(models_as_dict=False, use_nested_encoders=True) == custom_parent