Let's say I have a simple pydantic.BaseModel like this:
from myapp import User from pydantic import BaseModel, validator class ChangePasswordRequest(BaseModel): class Config: arbitrary_types_allowed = True # because I'm using my `User` arbitrary type user: User current_password: constr(min_length=1, max_length=255) new_password: constr(min_length=8, max_length=64) @validator("user") def user_is_active(cls, user: User): assert not user.inactive, "User is not active." return user @validator("current_password") def current_password_validator(cls, password: str, values: Dict[str, Any]): user: User = values["user"] assert user.check_password(password), "current_password is not valid." return password This is the model I'd like to use to handle user's password change. When I create ChangePasswordRequest with inactive user, I get a KeyError, because pydatinc validates all the fields.
from myapp import User user = User(inactive=True) r = ChangePasswordRequest(user=user, current_password="current", new_password="new-password") # I get KeyError: 'user' The reason why it happens:
user_is_activeis called and validation fails.values["user"]is not set as an effectcurrent_password_validatoris called.values["user"]key does not exist andKeyErroris raised.
What I want to achieve is to skip all validation if user field validation fails as there is no point of further validation. This is not a problem for a small model like mine as I can add an if statement in each validator, but this gets annoying as model grows.
@validator("current_password") def current_password_validator(cls, password: str, values: Dict[str, Any]): if not user := values.get("user"): return password assert user.check_password(password), "current_password is not valid." return password Other way would be to use root_validator(pre=True), but there is another problem with this approach. Error is located in the __root__ field, instead of user field.
@root_validator(pre=True) def user_is_active(cls, values: Dict[str, Any]): user: Union[User, None] = values.get("user", None) assert getattr(user, "is_active", False), "User is not active." return values # which produces following error: [{'loc': ['__root__'], 'msg': 'User is not active.', 'type': 'assertion_error'}] Does pydantic have any feature that will stop further validation if one of the "main" or "crucial" fields fails?
1 Answer
Instead of trying to stop execution after the first validator, you can begin the second one with something like this:
if "user" not in values: raise ValueError("Could not validate current_password because user did not pass validation")