【2.1】Pydantic使用方法

发布时间 2023-10-01 15:35:31作者: Chimengmeng

【一】介绍

Data validation and settings management using python type annotations.
使用Python的类型注解来进行数据校验和settings管理

pydantic enforces type hints at runtime, and provides user friendly errors when data is invalid.
Pydantic可以在代码运行时提供类型提示,数据校验失败时提供友好的错误提示

Define how data should be in pure, canonical python; validate it with pydantic.
定义数据应该如何在纯规范的Python代码中保存,并用Pydantic验证它

【二】类模型定义及使用

【1】使用 pydantic 语法创建模型类

# 导入 pydantic 中的 BaseModel 模型类
from pydantic import BaseModel
from datetime import datetime
from typing import List, Optional


# 【1】定义模型类
class User(BaseModel):
    # 定义模型类中的属性
    # 定义 ID 为 int 类型 , 没有指定 默认字段 则表示该字段为必填字段,初始化类时需要传入参数
    id: int  # 必填字段
    # 定义 name 为 str 类型并指定一个默认值
    name: str = 'dream'  # 选填字段
    # 定义 signup_time 为 datetime 类型 (需导入包 datetime) 并用 Optional 初始化
    signup_time: Optional[datetime] = None
    # 定义 friends 为 List 类型 并且规定元素类型为 int 同时指定一个默认空列表
    friends: List[int] = []  # 列表中的元素是int类型,或者可以直接转换成 int 类型
    '''
    注意字符串包裹的 int 类型 也可以 将 str 类型 强转成 int 类型
        比如 '1' ---> 1
    '''

【2】创建数据格式字典

# 【2】创建数据格式字典
external_data = {
    # id 自定义
    'id': "123",
    # 名称默认
    # signup_time 使用 自定义时间格式 --- 可以被识别的格式
    'signup_time': '2023-9-25 16:56:22',
    # friends 传入 int 类型  或者可以被 int 强转的 str 类型
    'friends': [1, 2, '3', '4']
}

【3】初始化模型类

# 【3】初始化模型类
user = User(**external_data)
print(f'当前对象 :>>> {user}')
print(f"当前对象属性 :>>>> {dir(user)}")

# 调用属性
print(user.id)
print(user.friends)
print(repr(user.signup_time))

# 输出对象数据为字典格式
print(user.dict())
当前对象 :>>> id=123 name='dream' signup_time=datetime.datetime(2023, 9, 25, 16, 56, 22) friends=[1, 2, 3, 4]

当前对象属性 :>>>> ['Config', '__abstractmethods__', '__annotations__', '__class__', '__config__', '__custom_root_type__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__fields__', '__fields_set__', '__format__', '__ge__', '__get_validators__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__json_encoder__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__post_root_validators__', '__pre_root_validators__', '__pretty__', '__private_attributes__', '__reduce__', '__reduce_ex__', '__repr__', '__repr_args__', '__repr_name__', '__repr_str__', '__schema_cache__', '__setattr__', '__setstate__', '__signature__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__validators__', '__values__', '_abc_impl', '_calculate_keys', '_decompose_class', '_get_value', '_init_private_attributes', '_iter', 'construct', 'copy', 'dict', 'fields', 'friends', 'from_orm', 'id', 'json', 'name', 'parse_file', 'parse_obj', 'parse_raw', 'schema', 'schema_json', 'signup_time', 'to_string', 'update_forward_refs', 'validate']

123

[1, 2, 3, 4]

datetime.datetime(2023, 9, 25, 16, 56, 22)

{'id': 123, 'name': 'dream', 'signup_time': datetime.datetime(2023, 9, 25, 16, 56, 22), 'friends': [1, 2, 3, 4]}

【4】模型类初识化遇到的异常处理

  • 如果传入的类型与模型中的定义不符
# 【2】创建数据格式字典
external_data = {
    # id 自定义
    'id': "a123",
    # 名称默认
    # signup_time 使用 自定义时间格式 --- 可以被识别的格式
    'signup_time': '2023-9-25 16:56:22',
    # friends 传入 int 类型  或者可以被 int 强转的 str 类型
    'friends': [1, 2, '3', '4']
}

user = User(**external_data)
  • 报错如下
value is not a valid integer (type=type_error.integer)

【5】序列化异常信息

  • 捕获到异常并序列化异常格式化输出
# 导入 pydantic 中的 BaseModel 模型类
from pydantic import BaseModel, ValidationError
from datetime import datetime
from typing import List, Optional


# 【1】定义模型类
class User(BaseModel):
    # 定义模型类中的属性
    # 定义 ID 为 int 类型 , 没有指定 默认字段 则表示该字段为必填字段,初始化类时需要传入参数
    id: int  # 必填字段
    # 定义 name 为 str 类型并指定一个默认值
    name: str = 'dream'  # 选填字段
    # 定义 signup_time 为 datetime 类型 (需导入包 datetime) 并用 Optional 初始化
    signup_time: Optional[datetime] = None
    # 定义 friends 为 List 类型 并且规定元素类型为 int 同时指定一个默认空列表
    friends: List[int] = []  # 列表中的元素是int类型,或者可以直接转换成 int 类型
    '''
    注意字符串包裹的 int 类型 也可以 将 str 类型 强转成 int 类型
        比如 '1' ---> 1
    '''
    
# 捕获到 格式 的异常并序列化输出
try:
    User(id=1, signup_time=datetime.today(), friends=['1', 2, 'not number'])
except ValidationError as e:
    print(f'捕获到异常 :>>>> {e.json()}')
捕获到异常 :>>>> [
  {
    "loc": [
      "friends",
      2
    ],
    "msg": "value is not a valid integer",
    "type": "type_error.integer"
  }
]

【6】模型类的方法介绍

# 导入 pydantic 中的 BaseModel 模型类
from pydantic import BaseModel, ValidationError
from datetime import datetime
from typing import List, Optional


# 【1】定义模型类
class User(BaseModel):
    # 定义模型类中的属性
    # 定义 ID 为 int 类型 , 没有指定 默认字段 则表示该字段为必填字段,初始化类时需要传入参数
    id: int  # 必填字段
    # 定义 name 为 str 类型并指定一个默认值
    name: str = 'dream'  # 选填字段
    # 定义 signup_time 为 datetime 类型 (需导入包 datetime) 并用 Optional 初始化
    signup_time: Optional[datetime] = None
    # 定义 friends 为 List 类型 并且规定元素类型为 int 同时指定一个默认空列表
    friends: List[int] = []  # 列表中的元素是int类型,或者可以直接转换成 int 类型
    '''
    注意字符串包裹的 int 类型 也可以 将 str 类型 强转成 int 类型
        比如 '1' ---> 1
    '''


# 【2】创建数据格式字典
external_data = {
    # id 自定义
    'id': "123",
    # 名称默认
    # signup_time 使用 自定义时间格式 --- 可以被识别的格式
    'signup_time': '2023-9-25 16:56:22',
    # friends 传入 int 类型  或者可以被 int 强转的 str 类型
    'friends': [1, 2, '3', '4']
}

# 【3】初始化模型类
user = User(**external_data)


print("\033[31m3. --- 模型类的的属性和方法 ---\033[0m")
  • 字典序列化
# 字典序列化
print(user.dict())
{'id': 123, 'name': 'dream', 'signup_time': datetime.datetime(2023, 9, 25, 16, 56, 22), 'friends': [1, 2, 3, 4]}
  • json序列化
# json序列化
print(user.json())
{"id": 123, "name": "dream", "signup_time": "2023-09-25T16:56:22", "friends": [1, 2, 3, 4]}
  • copy数据 (注意这里的 copy 是浅拷贝)
# copy数据 (注意这里的 copy 是浅拷贝)
print(user.copy())
id=123 name='dream' signup_time=datetime.datetime(2023, 9, 25, 16, 56, 22) friends=[1, 2, 3, 4]
  • 使用解包的方式解析数据
# 使用解包的方式解析数据 (使用 类去解析数据生成对象)
print(User.parse_obj(obj=external_data))
id=123 name='dream' signup_time=datetime.datetime(2023, 9, 25, 16, 56, 22) friends=[1, 2, 3, 4]
  • 解析原生的字符串数据
# 解析原生的数据 (使用 类去解析数据生成对象)
print(User.parse_raw('{"id": "123", "signup_ts": "2020-12-22 12:22", "friends": [1, 2, "3"]}'))
id=123 name='dream' signup_time=None friends=[1, 2, 3]
  • 解析文件中的数据
# 准备数据方式一:新建pydantic_data.json
# 写入如下数据
{"id": "123", "signup_ts": "2020-12-22 12:22", "friends": [1, 2, "3"]}

# 准备数据方式二:使用pathliub模块
from pathlib import Path

path = Path('pydantic_data.json')
path.write_text('{"id": "123", "signup_ts": "2020-12-22 12:22", "friends": [1, 2, "3"]}')
# 解析文件中的数据
print(User.parse_file('pydantic_data.json'))
id=123 name='dream' signup_time=None friends=[1, 2, 3]
  • 得到对象更多的信息
# 得到的对象包含更多的信息
print(user.schema())
print(user.schema_json())
{'title': 'User', 'type': 'object', 'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'name': {'title': 'Name', 'default': 'dream', 'type': 'string'}, 'signup_time': {'title': 'Signup Time', 'type': 'string', 'format': 'date-time'}, 'friends': {'title': 'Friends', 'default': [], 'type': 'array', 'items': {'type': 'integer'}}}, 'required': ['id']}

{"title": "User", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "default": "dream", "type": "string"}, "signup_time": {"title": "Signup Time", "type": "string", "format": "date-time"}, "friends": {"title": "Friends", "default": [], "type": "array", "items": {"type": "integer"}}}, "required": ["id"]}
  • 不检验数据直接创建模型类
# 不检验数据直接创建模型类,不建议在construct方法中传入未经验证的数据
# id是字符串 是错误的
user_data = {"id": "error", "signup_ts": "2020-12-22 12 22", "friends": [1, 2, 3]}
print(User.construct(**user_data))
name='dream' signup_time=None friends=[1, 2, 3] id='error' signup_ts='2020-12-22 12 22'
  • 查看当前模型类中的所有的字段
# 查看当前模型类中的所有的字段
# 定义模型类的时候,所有字段都注明类型,字段顺序就不会乱
print(User.__fields__.keys())
name='dream' signup_time=None friends=[1, 2, 3] id='error' signup_ts='2020-12-22 12 22'
dict_keys(['id', 'name', 'signup_time', 'friends'])

【三】递归模型

  • 在一个类模型中调用另一个类模型

【1】创建模型类

# 导入 pydantic 中的 BaseModel 模型类
from pydantic import BaseModel, ValidationError
from datetime import datetime, time
from typing import List, Optional

# 定义一个 叫声类
class Sound(BaseModel):
    sound: str

# 定义一个 狗 类
class Dog(BaseModel):
    birthday: time
    weight: float = Optional[None]
    # 不同的狗有不同的叫声。递归模型(Recursive Models)就是指一个嵌套一个
    # 狗的叫声有很多种,并且这些叫声都是 Sound 类型的
    sound: List[Sound]

【2】创建数据格式

dog_data = {
    'birthday': date.today(),
    'weight': 6.66,
    'sound': [{"sound": "wang wang ~"}, {"sound": "ying ying ~"}],
}

【3】初始化对象

dog = Dog(**dog_data)
print(dog.dict())
{'birthday': datetime.date(2023, 9, 28), 'sound': [{'sound': 'wang wang ~'}, {'sound': 'ying ying ~'}]}

【四】ORM类型

  • ORM模型:从类实例创建符合ORM对象的模型

【1】创建模型表

# 导入 pydantic 中的 BaseModel 模型类
from pydantic import BaseModel, constr
from typing import List

# 导入字段
from sqlalchemy import Column, String, Integer
# 导入元组类
from sqlalchemy.dialects.postgresql import ARRAY
# 声明
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


# 【1】创建表
class CompanyOrm(Base):
    # 表名
    __tablename__ = 'companies'
    # 创建 字段 ID 为 Integer 类型且为主键,不能为空
    id = Column(Integer, primary_key=True, nullable=False)
    # 创建 字段 public_key 为 String 类型 ,且指明长度 为 20 , index 建立索引,nullable 不能为空,unique 唯一
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    # 创建 字段 name 为 String 类型,指明长度 63 ,unique 唯一
    name = Column(String(63), unique=True)
    # 创建字段 domains 为 ARRAY (元组) 类型,且元组中的元素类型为 String 类型 ,指明长度 为 255
    domains = Column(ARRAY(String(255)))

【2】定义 ORM 模型表

# 【2】创建表 --- 定义一个表与上面的字段类型一样
class CompanyModel(BaseModel):
    id: int
    # constr 限制字符串
    public_key: constr(max_length=20)
    name = constr(max_length=63)
    domains: List[constr(max_length=255)]

    # 定义配置
    class Config:
        orm_mode = True

【3】创建数据字典

# 【3】创建数据字典
orm_data = {
    "id": "123",
    "public_key": "strawberry",
    "name": "Dream",
    "domains": ['example.com', 'dream.com'],
}

【4】实例化表模型对象

# 【4】实例化的到 表模型 对象
co_orm = CompanyOrm(**orm_data)

【5】实例化得到 ORM 对象

# 【5】实例化得到 ORM 对象
# ORM模型:从类实例创建符合ORM对象的模型
# co_orm 是 类的实例 使用 CompanyModel.from_orm 去校验他的数据类型
# pydantic 定义的模型类的规范
orm_obj = CompanyModel.from_orm(co_orm)
print(orm_obj)
id=123 public_key='strawberry' domains=['example.com', 'dream.com']

【6】补充

  • 提醒:选择你安装的 pydantic 版本的官方文档查看,新的版本对相关的类型进行了更新

【补充】Pydantic 插件

  • 在 Pycharm 的 插件商店中
    • 也有 Pydantic 插件,可以选择性安装

image-20230928170712308