Question: I've following pydantic model defined. When I run p = IntOrStr(value=True), I expect failure as True is boolean and it should fail the assignments to __int and __str class IntOrStr(BaseModel): __int: Optional[conint(strict=True, le=100, ge=10)] = None __str: Optional[constr(strict=True, max_length=64, min_length=10)] = None value: Any @validator("value") def value_must_be_int_or_str(cls, v): try: __int = v # no validation. not sure why? return v except ValidationError as e: print(str(e)) try: __str = v # no validation. not sure why? return v except ValidationError as e: print(str(e)) raise ValueError("error. value must be int or str") class Config: validate_assignment = True  Does anyone know why __int = v and __str = v do not trigger any validation? Thanks.

Answer: There are a quite a few problems here. Namespace This is unrelated to Pydantic; this is just a misunderstanding of how Python namespaces work: In the namespace of the method, __int and __str are just local variables. What you are doing is simply creating these variables and assigning values to them, then discarding them without doing anything with them. They are completely unrelated to the fields/attributes of your model. If you wanted to assign a value to a class attribute, you would have to do the following: class Foo: x: int = 0 @classmethod def method(cls) -> None: cls.x = 42  But that is not what you want in this case because... Class vs. instance A validator is a class method. This is hinted at by the first parameter being named cls even though the @classmethod decorator can be omitted with @validator. Thus, you will never be able to assign a value to any field of the model instance inside a validator, regardless of the validate_assignment configuration. The validator merely processes the value provided for assignment to the instance. What it returns may then eventually be assigned to the instance, if no other validators get in the way. If you want the value passed to one field to influence what is eventually assigned to other fields, you should instead use a @root_validator. Validator precedence You need to take into account the order in which validators are called. That order is determined by the order in which fields were defined. (see docs) Root validators are called after field validators by default. Thus, if you want changes done by a root validator to influence field validation, you need to use pre=True on it. Underscores Pydantic does not treat attributes, whose names start with an underscore, as fields, meaning they are not subject to validation. If you need a field name that starts with an underscore, you will have to use an alias. Working example All together, I guess you would need something more like the following: from typing import Any, Optional from pydantic import BaseModel, Field, ValidationError, conint, constr, root_validator class IntOrStr(BaseModel): a: Optional[ conint(strict=True, le=100, ge=10) ] = Field(default=None, alias="__a") b: Optional[ constr(strict=True, max_length=64, min_length=10) ] = Field(default=None, alias="__b") value: Any @root_validator(pre=True) def value_to_a_and_b(cls, values: dict[str, Any]) -> dict[str, Any]: value = values.get("value") values["__a"] = value values["__b"] = value return values if __name__ == "__main__": try: IntOrStr(value=True) except ValidationError as e: print(e)  The output: 2 validation errors for IntOrStr __a value is not a valid integer (type=type_error.integer) __b str type expected (type=type_error.str)  Note that in this setup, the errors are actually picked up by the individual default field validators for the conint and constr types. Also, in this simple example you would not be able to manually set __a or __b because the values would always be overridden in the root validator. But since I don't know your actual intention, I just set it up like this to trigger your desired validation error. Hope this helps.