如何验证pydantic模型的多个领域

sam*_*mba 5 python validation pydantic

我想验证pydantic模型的三个模型字段。为此,我正在从 pydantic 导入 root_validator。低于错误。我在https://pydantic-docs.helpmanual.io/usage/validators/#root-validators 中找到了这个。任何人都可以帮助我。找出下面的错误。from pydantic import BaseModel, ValidationError, root_validator Traceback (最近一次调用): File "", line 1, in ImportError: cannot import name 'root_validator' from 'pydantic' (C:\Users\Lenovo\AppData\Local\Programs\ Python\Python38-32\lib\site-packages\pydantic__init__.py)

我试过

@validator
def validate_all(cls,v,values,**kwargs):
Run Code Online (Sandbox Code Playgroud)

我从一些常见的字段父模型继承了我的 pydantic 模型。仅显示父类字段的值,但不显示我的子类字段。例如

class Parent(BaseModel):
    name: str
    comments: str
class Customer(Parent):
    address: str
    phone: str

    @validator
    def validate_all(cls,v,values, **kwargs):
         #here values showing only (name and comment) but not address and phone.
Run Code Online (Sandbox Code Playgroud)

wue*_*eli 41

为了扩展 的答案Rahul R,此示例更详细地展示了如何使用pydantic验证器。

此示例包含回答您的问题的所有必要信息。

请注意,还可以选择使用 a @root_validator,如 所提到的Kentgrav,请参阅帖子底部的示例以了解更多详细信息。

import pydantic

class Parent(pydantic.BaseModel):
    name: str
    comments: str

class Customer(Parent):
    address: str
    phone: str

    # If you want to apply the Validator to the fields "name", "comments", "address", "phone"
    @pydantic.validator("name", "comments", "address", "phone")
    @classmethod
    def validate_all_fields_one_by_one(cls, field_value):
        # Do the validation instead of printing
        print(f"{cls}: Field value {field_value}")

        return field_value  # this is the value written to the class field

    # if you want to validate to content of "phone" using the other fields of the Parent and Child class
    @pydantic.validator("phone")
    @classmethod
    def validate_one_field_using_the_others(cls, field_value, values, field, config):
        parent_class_name = values["name"]
        parent_class_address = values["address"] # works because "address" is already validated once we validate "phone"
        # Do the validation instead of printing
        print(f"{field_value} is the {field.name} of {parent_class_name}")

        return field_value 

Customer(name="Peter", comments="Pydantic User", address="Home", phone="117")
Run Code Online (Sandbox Code Playgroud)

输出

<class '__main__.Customer'>: Field value Peter
<class '__main__.Customer'>: Field value Pydantic User
<class '__main__.Customer'>: Field value Home
<class '__main__.Customer'>: Field value 117
117 is the phone number of Peter
Customer(name='Peter', comments='Pydantic User', address='Home', phone='117')
Run Code Online (Sandbox Code Playgroud)

更详细地回答您的问题:

将要验证的字段添加到@validator验证函数正上方的装饰器中。

  • @validator("name")"name"使用(eg )的字段值"Peter"作为验证函数的输入。类及其父类的所有字段都可以添加到@validator装饰器中。
  • 然后,验证函数 ( validate_all_fields_one_by_one) 使用字段值作为第二个参数 ( field_value) 来验证输入。验证函数的返回值写入类字段。验证函数的签名是def validate_something(cls, field_value)可以任意选择函数和变量名称的地方(但第一个参数应该是cls)。根据 Arjan ( https://youtu.be/Vj-iU-8_xLs?t=329 ) 的说法,还@classmethod应该添加装饰器。

如果目标是使用父类和子类的其他(已验证)字段来验证一个字段,则验证函数的完整签名是def validate_something(cls, field_value, values, field, config)(参数名称valuesfield并且config 必须匹配),可以使用以下方式访问字段的值字段名称作为键(例如values["comments"])。

Edit1:如果您只想检查某种类型的输入值,您可以使用以下结构:

<class '__main__.Customer'>: Field value Peter
<class '__main__.Customer'>: Field value Pydantic User
<class '__main__.Customer'>: Field value Home
<class '__main__.Customer'>: Field value 117
117 is the phone number of Peter
Customer(name='Peter', comments='Pydantic User', address='Home', phone='117')
Run Code Online (Sandbox Code Playgroud)

Edit2:使用以下方法一起验证所有字段的更简单方法@root_validator

@validator("*") # validates all fields
def validate_if_float(cls, value):
    if isinstance(value, float):
        # do validation here
    return value
Run Code Online (Sandbox Code Playgroud)

输出

import pydantic

class Parent(pydantic.BaseModel):
    name: str
    comments: str

class Customer(Parent):
    address: str
    phone: str

    @pydantic.root_validator()
    @classmethod
    def validate_all_fields_at_the_same_time(cls, field_values):
        # Do the validation instead of printing
        print(f"{cls}: Field values are: {field_values}")
        assert field_values["name"] != "invalid_name", f"Name `{field_values['name']}` not allowed."
        return field_values
Run Code Online (Sandbox Code Playgroud)
Customer(name="valid_name", comments="", address="Street 7", phone="079")
<class '__main__.Customer'>: Field values are: {'name': 'valid_name', 'comments': '', 'address': 'Street 7', 'phone': '079'}
Customer(name='valid_name', comments='', address='Street 7', phone='079')
Run Code Online (Sandbox Code Playgroud)

  • 我认为你不需要`@classmethod`装饰器,因为`@validator`已经返回了一个类方法;请参阅[此问题](https://github.com/pydantic/pydantic/issues/1415)。 (3认同)

小智 11

您需要将字段作为装饰器的参数传递。

class Parent(BaseModel):
    name: str
    comments: str

class Customer(Parent):
    address: str
    phone: str

    @validator("name", "coments", "address", "phone")
    def validate_all(cls, v, values, **kwargs):
Run Code Online (Sandbox Code Playgroud)

  • 如果您解释了您提供的代码如何回答问题,这将是一个更好的答案。 (24认同)

Chr*_*ris 8

选项 1 - 使用@validator装饰器

\n

根据文档,“通过传递多个字段名称,可以将单个字段validator应用于多个字段”(并且“也可以通过传递特殊值在所有\'*\'字段上调用”)。因此,您可以将想要验证的字段添加到validator装饰器中,并使用field.name属性您可以检查每次validator调用时要验证哪个字段。如果某个字段未通过验证,您可以raise ValueError“将捕获并用于填充”(请参阅​​此处的ValidationError“注释”部分)。如果您需要根据其他字段验证某个字段,则必须首先检查它们是否已使用方法进行验证,如本答案(更新 2)所示。下面演示了一个示例,其中验证、和number 等字段(基于提供的)。提供的正则表达式模式只是用于本演示目的的示例,并且基于答案。values.get()namecountry_codephonecountry_code

\n
from pydantic import BaseModel, validator\nimport re\n\nname_pattern = re.compile(r\'[a-zA-Z\\s]+$\')\ncountry_codes = {"uk", "us"}\nUK_phone_pattern = re.compile(r\'^(\\+44\\s?7\\d{3}|\\(?07\\d{3}\\)?)\\s?\\d{3}\\s?\\d{3}$\')  # UK mobile phone number. Valid example: +44 7222 555 555\nUS_phone_pattern = re.compile(r\'^(\\([0-9]{3}\\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$\')  # US phone number. Valid example: (123) 123-1234\nphone_patterns = {"uk": UK_phone_pattern, "us": US_phone_pattern}\n\nclass Parent(BaseModel):\n    name: str\n    comments: str\n    \nclass Customer(Parent):\n    address: str\n    country_code: str\n    phone: str\n\n    @validator(\'name\', \'country_code\', \'phone\')\n    def validate_atts(cls, v, values, field):\n        if field.name == "name":\n            if not name_pattern.match(v): raise ValueError(f\'{v} is not a valid name.\')\n        elif field.name == "country_code":\n             if not v.lower() in country_codes: raise ValueError(f\'{v} is not a valid country code.\')\n        elif field.name == "phone" and values.get(\'country_code\'):\n            c_code = values.get(\'country_code\').lower()\n            if not phone_patterns[c_code].match(v): raise ValueError(f\'{v} is not a valid phone number.\')\n        return v\n
Run Code Online (Sandbox Code Playgroud)\n

更新 - Pydantic V2 示例

\n

在 Pydantic V2 中,@validator已被弃用,并被@field_validator. 如果您想values从 中的另一个字段进行访问@field_validator,可以使用ValidationInfo.data,它是字段名称到字段值的字典。

\n
from pydantic import BaseModel, ValidationInfo, field_validator\nimport re\n\n# ... the rest of the code is the same as above\n\n\nclass Customer(Parent):\n    address: str\n    country_code: str\n    phone: str\n\n    @field_validator(\'name\', \'country_code\', \'phone\')\n    @classmethod\n    def validate_atts(cls, v: str, info: ValidationInfo):\n        if info.field_name == \'name\':\n            if not name_pattern.match(v): raise ValueError(f\'{v} is not a valid name.\')\n        elif info.field_name == \'country_code\':\n             if not v.lower() in country_codes: raise ValueError(f\'{v} is not a valid country code.\')\n        elif info.field_name == \'phone\' and info.data.get(\'country_code\'):\n            c_code = info.data.get(\'country_code\').lower()\n            if not phone_patterns[c_code].match(v): raise ValueError(f\'{v} is not a valid phone number.\')\n        return v\n
Run Code Online (Sandbox Code Playgroud)\n

选项 2 - 使用@root_validator装饰器

\n

另一种方法是使用@root_validator,它允许对整个模型的数据执行验证。

\n
from pydantic import BaseModel, root_validator\nimport re\n\nname_pattern = re.compile(r\'[a-zA-Z\\s]+$\')\ncountry_codes = {"uk", "us"}\nUK_phone_pattern = re.compile(r\'^(\\+44\\s?7\\d{3}|\\(?07\\d{3}\\)?)\\s?\\d{3}\\s?\\d{3}$\')  # UK mobile phone number. Valid example: +44 7222 555 555\nUS_phone_pattern = re.compile(r\'^(\\([0-9]{3}\\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$\')  # US phone number. Valid example: (123) 123-1234\nphone_patterns = {"uk": UK_phone_pattern, "us": US_phone_pattern}\n\nclass Parent(BaseModel):\n    name: str\n    comments: str\n    \nclass Customer(Parent):\n    address: str\n    country_code: str\n    phone: str\n\n    @root_validator()\n    def validate_atts(cls, values):\n        name = values.get(\'name\')\n        comments = values.get(\'comments\')\n        address = values.get(\'address\')\n        country_code = values.get(\'country_code\')\n        phone = values.get(\'phone\')\n        \n        if name is not None and not name_pattern.match(name): \n            raise ValueError(f\'{name} is not a valid name.\')\n        if country_code is not None and not country_code.lower() in country_codes: \n            raise ValueError(f\'{country_code} is not a valid country code.\')\n        if phone is not None and country_code is not None:\n            if not phone_patterns[country_code.lower()].match(phone): \n                raise ValueError(f\'{phone} is not a valid phone number.\')\n                \n        return values\n
Run Code Online (Sandbox Code Playgroud)\n

更新 - Pydantic V2 示例

\n

在 Pydantic V2 中,@root_validator已被弃用,并被@model_validator. 模型验证器可以是mode=\'before\',mode=\'after\'mode=\'wrap\'。在这种情况下,mode=\'after\'最适合。如文档中所述:

\n
\n

mode=\'after\'验证器是实例方法,并且始终接收模型的\实例作为第一个参数。您不应将(cls, ModelType)其用作签名,而应仅使用并让类型\n检查器为您(self)推断 的类型。由于它们是完全类型安全的,因此它们通常比验证器self更容易实现。mode=\'before\'如果任何字段验证失败,mode=\'after\'则不会调用该字段的\n验证器。

\n
\n
使用 mode=\'after\'
\n
from pydantic import BaseModel, model_validator\nimport re\n\n# ... the rest of the code is the same as above\n\n\nclass Customer(Parent):\n    address: str\n    country_code: str\n    phone: str\n\n    @model_validator(mode=\'after\')\n    def validate_atts(self):\n        name = self.name\n        comments = self.comments\n        address = self.address\n        country_code = self.country_code\n        phone = self.phone\n        \n        if name is not None and not name_pattern.match(name): \n            raise ValueError(f\'{name} is not a valid name.\')\n        if country_code is not None and not country_code.lower() in country_codes: \n            raise ValueError(f\'{country_code} is not a valid country code.\')\n        if phone is not None and country_code is not None:\n            if not phone_patterns[country_code.lower()].match(phone): \n                raise ValueError(f\'{phone} is not a valid phone number.\')\n                \n        return self\n
Run Code Online (Sandbox Code Playgroud)\n
使用 mode=\'before\'
\n

如果您更愿意使用mode=\'before,您可以按如下方式操作。但请注意,在这种情况下,您应该在继续进一步str处理/验证(例如,将值转换为小写、字符串值比较等)\xe2\x80\x94 不包含在下面。

\n
from pydantic import BaseModel, model_validator\nfrom typing import Any\nimport re\n\n# ... the rest of the code is the same as above\n\n\nclass Customer(Parent):\n    address: str\n    country_code: str\n    phone: str\n\n    @model_validator(mode=\'before\')\n    @classmethod\n    def validate_atts(cls, data: Any):\n        if isinstance(data, dict):\n            name = data.get(\'name\')\n            comments = data.get(\'comments\')\n            address = data.get(\'address\')\n            country_code = data.get(\'country_code\')\n            phone = data.get(\'phone\')\n            \n            if name is not None and not name_pattern.match(name): \n                raise ValueError(f\'{name} is not a valid name.\')\n            if country_code is not None and not country_code.lower() in country_codes: \n                raise ValueError(f\'{country_code} is not a valid country code.\')\n            if phone is not None and country_code is not None:\n                if not phone_patterns[country_code.lower()].match(phone): \n                    raise ValueError(f\'{phone} is not a valid phone number.\')\n                \n        return data\n
Run Code Online (Sandbox Code Playgroud)\n

选项 1 和 2 的测试示例

\n
from pydantic import ValidationError\n\n# should throw "Value error, (123) 123-1234 is not a valid phone number."\ntry:\n    Customer(name=\'john\', comments=\'hi\', address=\'some address\', country_code=\'UK\', phone=\'(123) 123-1234\')\nexcept ValidationError as e:\n    print(e)\n\n# should work without errors\nprint(Customer(name=\'john\', comments=\'hi\', address=\'some address\', country_code=\'UK\', phone=\'+44 7222 555 555\'))\n
Run Code Online (Sandbox Code Playgroud)\n


Ken*_*rav 6

首先,如果您在导入root_validator时遇到错误,我会更新 pydantic。

pip install -U pydantic
Run Code Online (Sandbox Code Playgroud)

上面的许多示例向您展示了如何一次对多个值使用相同的验证器。或者它们会增加很多不必要的复杂性来完成你想要的事情。您可以简单地使用以下代码使用root_validator装饰器在同一验证器中同时验证多个字段:

from pydantic import root_validator
from pydantic import BaseModel

class Parent(BaseModel):
    name: str = "Peter"
    comments: str = "Pydantic User"

class Customer(Parent):
    address: str = "Home"
    phone: str = "117"

    @root_validator
    def validate_all(cls, values):
         print(f"{values}")
         values["phone"] = "111-111-1111"
         values["address"] = "1111 Pydantic Lane"
         print(f"{values}")
         return values

Output:

{'name': 'Peter', 'comments': 'Pydantic User', 'address': 'Home', 'phone': '117'}

{'name': 'Peter', 'comments': 'Pydantic User', 'address': '1111 Pydantic Lane', 'phone': '111-111-1111'}
Run Code Online (Sandbox Code Playgroud)