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]