Skip to content

Commit

Permalink
fix: check only first sublevel for validators with each_item
Browse files Browse the repository at this point in the history
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 pydantic#1933
  • Loading branch information
PrettyWood committed Oct 11, 2020
1 parent e8326f8 commit 9b9495b
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 1 deletion.
2 changes: 2 additions & 0 deletions 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.
18 changes: 17 additions & 1 deletion pydantic/fields.py
Expand Up @@ -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,
)

Expand Down
13 changes: 13 additions & 0 deletions tests/test_validators.py
Expand Up @@ -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]
Expand Down

0 comments on commit 9b9495b

Please sign in to comment.