From 9b9495b7fa81f4c2436f99a0277f0981659bb371 Mon Sep 17 00:00:00 2001 From: PrettyWood Date: Sun, 11 Oct 2020 01:55:33 +0200 Subject: [PATCH] fix: check only first sublevel for validators with `each_item` When using a validator with `each_item`, the items are all validated one by one. But if the items are also iterable the subitems would then be validated because the validator would be kept as it is. Now the validator passed to the items is changed and won't be propagated closes #1933 --- changes/1933-PrettyWood.md | 2 ++ pydantic/fields.py | 18 +++++++++++++++++- tests/test_validators.py | 13 +++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 changes/1933-PrettyWood.md diff --git a/changes/1933-PrettyWood.md b/changes/1933-PrettyWood.md new file mode 100644 index 0000000000..cd8b882b7b --- /dev/null +++ b/changes/1933-PrettyWood.md @@ -0,0 +1,2 @@ +Validate only sublevel items with `each_item` instead of all sublevels, which +would happen when the sublevel items were also iterable. \ No newline at end of file diff --git a/pydantic/fields.py b/pydantic/fields.py index 2619b4b167..c519236501 100644 --- a/pydantic/fields.py +++ b/pydantic/fields.py @@ -504,10 +504,26 @@ def _type_analysis(self) -> None: # noqa: C901 (ignore complexity) self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)] def _create_sub_type(self, type_: Type[Any], name: str, *, for_keys: bool = False) -> 'ModelField': + if for_keys: + class_validators = None + else: + # validators for sub items should not have `each_item` as we want to check only the first sublevel + class_validators = { + k: Validator( + func=v.func, + pre=v.pre, + each_item=False, + always=v.always, + check_fields=v.check_fields, + skip_on_failure=v.skip_on_failure, + ) + for k, v in self.class_validators.items() + if v.each_item + } return self.__class__( type_=type_, name=name, - class_validators=None if for_keys else {k: v for k, v in self.class_validators.items() if v.each_item}, + class_validators=class_validators, model_config=self.model_config, ) diff --git a/tests/test_validators.py b/tests/test_validators.py index 5b700e8a22..36e07e0a51 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -536,6 +536,19 @@ def check_foobar(cls, v): assert Model(foobar={1: 1}).foobar == {1: 2} +def test_validation_each_item_one_sublevel(): + class Model(BaseModel): + foobar: List[Tuple[int, int]] + + @validator('foobar', each_item=True) + def check_foobar(cls, v: Tuple[int, int]) -> Tuple[int, int]: + v1, v2 = v + assert v1 == v2 + return v + + assert Model(foobar=[(1, 1), (2, 2)]).foobar == [(1, 1), (2, 2)] + + def test_key_validation(): class Model(BaseModel): foobar: Dict[int, int]