python pydantic模型验证器中字段赋值的验证

aydmsdu9  于 2023-02-18  发布在  Python
关注(0)|答案(1)|浏览(238)

我已经定义了pydantic模型。当我运行p = IntOrStr(value=True)时,我预期会失败,因为True是布尔值,它应该会失败对__int__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

有人知道为什么__int = v__str = v不触发任何验证吗?
谢谢。

s4n0splo

s4n0splo1#

这里有不少问题。

命名空间

这与Pydantic无关;这只是对Python名称空间工作方式的误解:
在方法的命名空间中,__int__str只是局部变量,您所做的只是创建这些变量并为它们赋值,然后丢弃它们而不对它们做任何操作。
它们与模型的字段/属性完全无关。
如果要为类属性赋值,则必须执行以下操作:

class Foo:
    x: int = 0
    @classmethod
    def method(cls) -> None:
        cls.x = 42

但这不是你想要的因为...

类与示例

验证器是一个类方法,这是由第一个名为cls的参数所暗示的,即使@classmethod装饰器可以在@validator中省略。
因此,无论validate_assignment的配置如何,你都无法在验证器中为模型示例的任何字段 * 赋值 *,验证器只是 * 处理 * 提供给示例赋值的值,如果没有其他验证器的阻碍,它返回的值 * 可能 * 最终被赋值给示例。
如果希望传递给一个字段的值影响最终分配给 * other * 字段的值,则应该使用@root_validator

验证器优先级

你需要考虑验证器被调用的顺序。这个顺序是由定义 * 字段 * 的顺序决定的。(参见docs
默认情况下根验证器在字段验证器之后被称为 * after * field validators,因此,如果你想让根验证器所做的更改影响字段验证,你需要在它上面使用pre=True

下划线

Pydantic不将名称以下划线开头的属性视为字段,这意味着它们不受验证的约束。如果需要以下划线开头的字段名,则必须使用别名。

工作示例

总的来说,我猜您需要的东西更像是:

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)

输出:

2 validation errors for IntOrStr
__a
  value is not a valid integer (type=type_error.integer)
__b
  str type expected (type=type_error.str)

注意,在这个设置中,错误实际上是由conintconstr类型的 * 单独 * 默认字段验证器拾取的。
另外,在这个简单的例子中,您不能手动设置__a__b,因为这些值总是在根验证器中被覆盖,但是由于我不知道您的实际意图,我只是这样设置它来触发您想要的验证错误。
希望这个有用。

相关问题