📜  在Python中使用 Pydantic 模块创建自定义数据类型

📅  最后修改于: 2022-05-13 01:55:18.112000             🧑  作者: Mango

在Python中使用 Pydantic 模块创建自定义数据类型

很多时候,我们发现我们需要将一长串变量传递给函数,并且在Python函数签名中指定所有这些可能有点混乱。同样,当您想要对传递的变量进行某种验证时,也会出现问题。对于一长串变量,在主函数体内部持续验证数据真的很困难,这也不是一个好习惯。在这种情况下,您要做的是将变量分离并隔离到不同的类中。在这里,我们将演示如何使用pydantic创建模型以及您的自定义验证。首先,让我们讨论一下用例。

考虑一下,我们正在从 API 调用中接收一些数据,我们需要对其进行某种分析。通常,API 响应将以 JSON 的形式发送响应,因此我们希望我们的模型能够序列化和反序列化 JSON (1)

此外,我们会假设某些变量的类型。例如,如果我们传递一个地址,我们会假设 pincode 是一个整数值。这是类型检查 (2)

要执行分析,您将对数据做出一些假设,例如,密码应与提供的地区名称匹配。这是验证 (3)

我们还可以假设,对于某些领域,比如州,它应该在印度的州列表中,而不是任何随机的任意值。这属于清洁 (4)

所以,有了这四个要求,让我们开始编码模式。我假设你的系统上安装了Python 。要安装pydantic ,只需运行,

pip install pydantic

使用该设置,创建一个名为models.py的文件并将以下代码粘贴到其中。我们在代码本身中添加了详细的内联注释,以便于直接理解。

Python3
# import required modules
from enum import Enum
from typing import Optional
from pydantic import BaseModel, PositiveInt, validator, root_validator, constr
 
 
 
# custom class used as choices for state
# pydantic choices using the built-in Enum of python
# which reduces the need for additional packages
class StateTypes(str, Enum):
 
    DELHI = "DLH"
    UTTAR_PRADESH = "UP"
    BENGALURU = "BLR"
    WEST_BENGAL = "WB"
 
     
     
# class to get personal credentials
class PersonalDetails(BaseModel):
    id: int
     
    # constr gives us the ability to specify
    # the min and max length
    name: constr(min_length=2, max_length=15
    phone: PositiveInt
 
    # validation at field level
    @validator("phone")
     
    # get phone number
    def phone_length(cls, v):
         
        # phone number should typically be of length 10
        if len(str(v)) != 10:
            raise ValueError("Phone number must be of ten digits")
        return v
 
 
                  
# class to get address                
class Address(BaseModel):
    id: int
    address_line_1: constr(max_length=50)
     
    # assigning some fields to be optional
    address_line_2: Optional[constr(max_length=50)] = None
    pincode: PositiveInt
    city: constr(max_length=30)
     
    # using choices in python is this simple.
    # Just create a class with Enums as choices
    # and the pass the class as type for the field
    state: StateTypes
 
    @validator("pincode")
    def pincode_length(cls, v):
        if len(str(v)) != 6:
            raise ValueError("Pincode must be of six digits")
        return v
 
                  
 
# using BaseModels as custom datatypes
# in the User class                
class User(BaseModel):
     
    personal_details: PersonalDetails
    address: Address
 
    @root_validator(skip_on_failure=True)
     
    # skip_on_failure=True means it will skip the
    # validation for this class if it's custom
    # fields are not validated
    def check_id(cls, values):
         
        # custom validation ensuring personal_details.id is
        # same as address.id
        personal_details: PersonalDetails = values.get("personal_details")
        address: Address = values.get("address")
        if personal_details.id != address.id:
            raise ValueError(
                "ID field of both personal_details as well as address should match"
            )
        return values
 
                  
 
# Driver Code
if __name__ == "__main__":
 
# testing models                
    validated_data = {
        "personal_details": {
            "id": 1,
            "name": "GeeksforGeeks",
            "phone": 9999999999,
        },
        "address": {
            "id": 1,
            "address_line_1": "Sector- 136",
            "pincode": 201305,
            "city": "Noida",
            "state": "UP",
        },
    }
     
    # this would work without any error as
    # no validation will fail
    user = User(**validated_data)
     
    # would print the standard __str__ value for the model
    print(user)
 
    unvalidated_data = {
        "personal_details": {
            "id": 1,
            "name": "GeeksforGeeks",
            "phone": 9999999999,
        },
        "address": {
            "id": 2,
            "address_line_1": "Sector- 136",
            "pincode": 201305,
            "city": "Noida",
            "state": "UP",
        },
    }
     
    # this would raise a value error since the IDs
    # are different
    user = User(**unvalidated_data)
    print(user)


输出:

执行截图

运行此命令后,第一个打印语句将成功执行,但在User模型的下一次初始化中,它将抛出ValueError类型的ValidationError ,因为个人详细信息和地址的ID不匹配。