• pydantic学习与使用4.validator 验证器的使用(pre 和 each_itemm 验证器) 上海


    前言

    validator 使用装饰器可以实现自定义验证和对象之间的复杂关系。

    验证器

    1.校验name字段包含空格
    2.校验username 必须是字母和数字组成
    3.校验密码1和密码2相等

    from pydantic import BaseModel, ValidationError, validator
    
    
    class UserModel(BaseModel):
        name: str
        username: str
        password1: str
        password2: str
    
        @validator('name')
        def name_must_contain_space(cls, v):
            if ' ' not in v:
                raise ValueError('must contain a space')
            return v.title()
    
        @validator('password2')
        def passwords_match(cls, v, values, **kwargs):
            if 'password1' in values and v != values['password1']:
                raise ValueError('passwords do not match')
            return v
    
        @validator('username')
        def username_alphanumeric(cls, v):
            assert v.isalnum(), 'must be alphanumeric'
            return v
    

    关于验证器的一些注意事项:

    • 验证器是“类方法”,因此它们接收的第一个参数值是UserModel类(cls),而不是UserModel的实例(self)
    • 第二个参数始终是要验证的字段值;可以随意命名
    • 您还可以将以下参数的任何子集添加到签名中(名称必须匹配):
      values:包含任何先前验证字段的名称到值映射的字典
      config:模型配置
      field:正在验证的字段。对象的类型是pydantic.fields.ModelField。
      **kwargs:如果提供,这将包括上述未在签名中明确列出的参数
    • 验证器应该返回解析后的值或引发 a ValueError, TypeError, or AssertionError (assert可以使用语句)。
    • 在验证器依赖其他值的情况下,您应该注意:
      验证是在定义的订单字段中完成的。例如,在上面的示例中,password2可以访问password1(and name),但password1不能访问password2. 有关字段如何排序 的更多信息,请参阅字段排序
      如果另一个字段的验证失败(或该字段丢失),它将不会包含在 中values,因此 if 'password1' in values and ...在此示例中。
      运行示例
    user = UserModel(
        name='samuel colvin',
        username='scolvin',
        password1='zxcvbn',
        password2='zxcvbn',
    )
    print(user)  
    print(user.dict())
    
    运行结果:
    name='Samuel Colvin' username='scolvin' password1='zxcvbn' password2='zxcvbn'
    {'name': 'Samuel Colvin', 'username': 'scolvin', 'password1': 'zxcvbn', 'password2': 'zxcvbn'}
    

    pre 和 each_item 验证器

    验证器可以做一些更复杂的事情:

    • 通过传递多个字段名称,可以将单个验证器应用于多个字段
    • 也可以通过传递特殊值在所有字段上调用单个验证器'*'
    • 关键字参数pre将导致验证器在其他验证之前被调用
    • 传递each_item=True将导致验证器应用于单个值(例如 of List、Dict、Set等),而不是整个对象

    pre=True 关键字参数pre将导致验证器在其他验证之前被调用

    from pydantic import BaseModel, ValidationError, validator
    from typing import List
    
    
    class DemoModel(BaseModel):
        friends: List[int] = []
        books: List[int] = []
    
        # '*' 在这里是匹配任意字段,包含friends,books
        @validator('*', pre=True)
        def split_str(cls, v):
            """如果传参是字符串,根据逗号切割成list"""
            if isinstance(v, str):
                return v.split(',')
            return v
    
        @validator('books')
        def books_greater_then_5(cls, v):
            """判断books数量少于5"""
            if len(v) > 5:
                raise ValueError('books greater than 5')
            return v
    
    
    a1 = {
        "friends": [2, 3, 4],
        "books": "3,4,5"
    }
    d = DemoModel(**a1)
    print(d)  # friends=[2, 3, 4] books=[3, 4, 5]
    print(d.dict())  # {'friends': [2, 3, 4], 'books': [3, 4, 5]}
    

    虽然定义了books传list of int ,但是在校验的时候,加了个预处理,判断是字符串的时候,会转成list。

    each_item=True 将导致验证器应用于单个值(例如 of List、Dict、Set等),而不是整个对象

    from pydantic import BaseModel, ValidationError, validator
    from typing import List
    
    
    class DemoModel(BaseModel):
        friends: List[int] = []
        books: List[int] = []
    
        # '*' 在这里是匹配任意字段,包含friends,books
        @validator('*', pre=True)
        def split_str(cls, v):
            """如果传参是字符串,根据逗号切割成list"""
            if isinstance(v, str):
                return v.split(',')
            return v
    
        @validator('books')
        def books_greater_then_5(cls, v):
            """判断books数量少于5"""
            if len(v) > 5:
                raise ValueError('books greater than 5')
            return v
    
        @validator('friends', each_item=True)
        def check_friends(cls, v):
            """检查friends 里面单个值数字大于1"""
            assert v >= 1, f'{v} is not greater then 1'
            return v
    
        @validator('books', each_item=True)
        def check_books(cls, v):
            """books 里面单个值大于2"""
            assert v >= 2, f'{v} is not greater then 2'
            return v
    
    
    a1 = {
        "friends": [2, 3, 4],
        "books": "3,4,5"
    }
    d = DemoModel(**a1)
    print(d)  # friends=[2, 3, 4] books=[3, 4, 5]
    print(d.dict())  # {'friends': [2, 3, 4], 'books': [3, 4, 5]}
    

    validator传递多个字段名称,也可以传*

    # '*' 在这里是匹配任意字段,包含friends,books
        @validator('*', pre=True)
        def split_str(cls, v):
            """如果传参是字符串,根据逗号切割成list"""
            if isinstance(v, str):
                return v.split(',')
            return v
    

    等价于

    @validator('friends', 'books', pre=True)
        def split_str(cls, v):
            """如果传参是字符串,根据逗号切割成list"""
            if isinstance(v, str):
                return v.split(',')
            return v
    

    子类验证器和each_item

    如果使用带有引用List父类上的类型字段的子类的验证器,使用each_item=True将导致验证器不运行;相反,必须以编程方式迭代列表。

    from typing import List
    from pydantic import BaseModel, ValidationError, validator
    
    
    class ParentModel(BaseModel):
        names: List[str]
    
    
    class ChildModel(ParentModel):
        @validator('names', each_item=True)
        def check_names_not_empty(cls, v):
            assert v != '', 'Empty strings are not allowed.'
            return v
    
    
    # This will NOT raise a ValidationError because the validator was not called
    try:
        child = ChildModel(names=['Alice', 'Bob', 'Eve', ''])
    except ValidationError as e:
        print(e)
    else:
        print('No ValidationError caught.')
        #> No ValidationError caught.
    
    
    class ChildModel2(ParentModel):
        @validator('names')
        def check_names_not_empty(cls, v):
            for name in v:
                assert name != '', 'Empty strings are not allowed.'
            return v
    
    
    try:
        child = ChildModel2(names=['Alice', 'Bob', 'Eve', ''])
    except ValidationError as e:
        print(e)
        """
        1 validation error for ChildModel2
        names
          Empty strings are not allowed. (type=assertion_error)
        """
    

    始终验证always=True

    出于性能原因,默认情况下,当未提供值时,不会为字段调用验证器。但是,在某些情况下,始终调用验证器可能很有用或需要,例如设置动态默认值。

    from datetime import datetime
    
    from pydantic import BaseModel, validator
    
    
    class DemoModel(BaseModel):
        ts: datetime = None
    
        @validator('ts', pre=True, always=True)
        def set_ts_now(cls, v):
            return v or datetime.now()
    
    
    print(DemoModel())
    #> ts=datetime.datetime(2021, 12, 31, 15, 4, 57, 629206)
    print(DemoModel(ts='2017-11-08T14:00'))
    #> ts=datetime.datetime(2017, 11, 8, 14, 0)
    

    您经常希望将它与 一起使用pre,否则always=True pydantic会尝试验证None会导致错误的默认值。

  • 相关阅读:
    Django models中的null和blank的区别
    3次登陆锁定与backend增删改查
    Python全栈考试(一)
    python第一天几个小游戏
    linux开发脚本自动部署及监控
    Linux awk&shell script
    linux正则表达式grep&sed
    linux网路IP.设定主机名.ssh .bash命令&通配符
    Linux:nginx(web服务),nfs服务+反向代理+负载均衡
    Linux内存dd,rpm,yum,软件安装
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/15917392.html
Copyright © 2020-2023  润新知