diff --git a/changes/1545-dcHHH.md b/changes/1545-dcHHH.md new file mode 100644 index 0000000..a5ccad5 --- /dev/null +++ b/changes/1545-dcHHH.md @@ -0,0 +1 @@ +Move the assignment of `field.validate_always` in `fields.py` so the `always` parameter of validators work on inheritance. diff --git a/pydantic/fields.py b/pydantic/fields.py index 67a3c13..99289b7 100644 --- a/pydantic/fields.py +++ b/pydantic/fields.py @@ -365,10 +365,6 @@ class ModelField(Representation): # user will need to call model.update_forward_refs() return - self.validate_always = getattr(self.type_, 'validate_always', False) or any( - v.always for v in self.class_validators.values() - ) - if self.required is False and default_value is None: self.allow_none = True @@ -511,6 +507,10 @@ class ModelField(Representation): and class validators. This method should be idempotent, e.g. it should be safe to call multiple times without mis-configuring the field. """ + self.validate_always = getattr(self.type_, 'validate_always', False) or any( + v.always for v in self.class_validators.values() + ) + class_validators_ = self.class_validators.values() if not self.sub_fields or self.shape == SHAPE_GENERIC: get_validators = getattr(self.type_, '__get_validators__', None) diff --git a/tests/test_validators.py b/tests/test_validators.py index 2ff9359..b50e80d 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -287,6 +287,25 @@ def test_validate_always(): assert check_calls == 2 +def test_validate_always_on_inheritance(): + check_calls = 0 + + class ParentModel(BaseModel): + a: str = None + + class Model(ParentModel): + @validator('a', pre=True, always=True) + def check_a(cls, v): + nonlocal check_calls + check_calls += 1 + return v or 'xxx' + + assert Model().a == 'xxx' + assert check_calls == 1 + assert Model(a='y').a == 'y' + assert check_calls == 2 + + def test_validate_not_always(): check_calls = 0