简体   繁体   English

如何验证 pydantic model 的多个字段

[英]How to validate more than one field of pydantic model

I want to validate three model Fields of pydantic model.我想验证 pydantic model 的三个 model 字段。 To Do this i am importing root_validator from pydantic.为此,我从 pydantic 导入 root_validator。 Getting below error.低于错误。 I found this in the https://pydantic-docs.helpmanual.io/usage/validators/#root-validators .我在https://pydantic-docs.helpmanual.io/usage/validators/#root-validators中找到了这个。 Could any one help me.任何人都可以帮助我。 Find the error below.找出下面的错误。 from pydantic import BaseModel, ValidationError, root_validator Traceback (most recent call last): 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) from pydantic import BaseModel, ValidationError, root_validator Traceback(最近一次调用最后一次):文件“”,第 1 行,在 ImportError 中:无法从“pydantic”导入名称“root_validator”(C:\Users\Lenovo\AppData\Local\Programs\ Python\Python38-32\lib\site-packages\pydantic__init__.py)

I tried in我试过了

@validator
def validate_all(cls,v,values,**kwargs):

I am inheriting my pydantic model from some common fields parent model.我从一些常见的字段父 model 继承了我的 pydantic model。 Values showing only parent class fields, but not my child class fields.值仅显示父 class 字段,但不显示我的子 class 字段。 for example例如

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.

You need to pass the fields as arguments of the decorator.您需要将字段作为装饰器的 arguments 传递。

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):

First off, if you are having an error importing root_validator , I would update pydantic.首先,如果您在导入root_validator时出错,我会更新 pydantic。

pip install -U pydantic

A lot of the examples above show you how to use the same validator on multiple values one at a time.上面的许多示例向您展示了如何一次对多个值使用相同的验证器。 Or they add a lot of unnecessary complexity to accomplish what you want.或者他们增加了很多不必要的复杂性来完成你想要的。 You can simply use the following code to validate multiple fields at the same time in the same validator using the root_validator decorator.:您可以简单地使用以下代码,使用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'}

To extend on the answer of Rahul R , this example shows in more detail how to use the pydantic validators.为了扩展Rahul R的答案,这个例子更详细地展示了如何使用pydantic验证器。

This example contains all the necessary information to answer your question.此示例包含回答您的问题所需的所有信息。

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")

Output Output

<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')

To answer your question in more detail:要更详细地回答您的问题:

Add the fields to validate to the @validator decorator directly above the validation function.将要验证的字段添加到验证@validator正上方的 @validator 装饰器。

  • @validator("name") uses the field value of "name" (eg "Peter" ) as input to the validation function. @validator("name")使用 " "name" " 的字段值(例如"Peter" )作为验证 function 的输入。 All fields of the class and its parent classes can be added to the @validator decorator. class 及其父类的所有字段都可以添加到@validator装饰器中。
  • the validation function ( validate_all_fields_one_by_one ) then uses the field value as the second argument ( field_value ) for which to validate the input.验证 function ( validate_all_fields_one_by_one ) 然后使用字段值作为第二个参数 ( field_value ) 来验证输入。 The return value of the validation function is written to the class field.验证 function 的返回值写入 class 字段。 The signature of the validation function is def validate_something(cls, field_value) where the function and variable names can be chosen arbitrarily (but the first argument should be cls ).验证 function 的签名是def validate_something(cls, field_value)其中 function 和变量名称可以任意选择(但第一个参数应该是cls )。 According to Arjan ( https://youtu.be/Vj-iU-8_xLs?t=329 ), also the @classmethod decorator should be added.根据 Arjan ( https://youtu.be/Vj-iU-8_xLs?t=329 ),还应该添加@classmethod装饰器。

If the goal is to validate one field by using other (already validated) fields of the parent and child class, the full signature of the validation function is def validate_something(cls, field_value, values, field, config) (the argument names values , field and config must match) where the value of the fields can be accessed with the field name as key (eg values["comments"] ).如果目标是通过使用父子 class 的其他(已验证)字段来验证一个字段,则验证 function 的完整签名是def validate_something(cls, field_value, values, field, config) (参数名称valuesfieldconfig必须匹配)可以使用字段名称作为键访问字段的值(例如values["comments"] )。

This example contains all the necessary information to answer your question.此示例包含回答您的问题所需的所有信息。

    class User(BaseModel):
        name: Optional[str] = ""

        class Config:
            validate_assignment = True

        @validator("name")
            def set_name(cls, name):
            return name or "foo"

As per the documentation , "a single validator can be applied to multiple fields by passing it multiple field names" (and "can also be called on all fields by passing the special value '*' ").根据文档,“可以通过传递多个字段名称将单个validator应用于多个字段”(并且“也可以通过传递特殊值'*'所有字段上调用”)。 Thus, you could add the fields you wish to validate to the validator decorator, and using field.name attribute you can check which one to validate each time the validator is called.因此,您可以将希望验证的字段添加到validator装饰器,并使用field.name属性,您可以检查每次调用validator器时要验证的字段。 If a field does not pass the validation, you could raise ValueError , "which will be caught and used to populate ValidationError " (see "Note" section here ).如果一个字段没有通过验证,您可以raise ValueError ,“这将被捕获并用于填充ValidationError ”(请参阅此处的“注意”部分)。 If you need to validate a field based on other field(s), you have to check first if they have already been validated using values.get() method, as shown in this answer (Update 2) .如果您需要根据其他字段验证字段,则必须首先检查它们是否已经使用values.get()方法进行了验证,如本答案所示(更新 2) The below demonstrates an example, where fields such as name , country_code , and phone number (based on the provided country_code ) are validated.下面演示了一个示例,其中验证了namecountry_codephone号码等字段(基于提供的country_code )。 The regex patterns provided are just examples for the purposes of this demo, and are based on this and this answer..提供的正则表达式模式只是本演示的示例,并且基于这个这个答案..

from pydantic import BaseModel, validator
import re

name_pattern = re.compile(r'[a-zA-Z\s]+$')
country_codes = {"uk", "us"}
UK_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
US_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
phone_patterns = {"uk": UK_phone_pattern, "us": US_phone_pattern}

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

    @validator("name", "country_code", "phone")
    def validate_atts(cls, v, values, field):
        if field.name == "name":
            if not name_pattern.match(v): raise ValueError(f'{v}" is not a valid name.')
        elif field.name == "country_code":
             if not v.lower() in country_codes: raise ValueError(f'{v} is not a valid country code.')
        elif field.name == "phone" and values.get('country_code'):
            c_code = values.get('country_code').lower()
            if not phone_patterns[c_code].match(v): raise ValueError(f'{v} is not a valid phone number.')
        return v

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM