Python 利用Pydantic模块提升开发效率




Pydantic 核心概念

Pydantic 是一个数据验证和设置管理库,它的核心概念是数据模型(Model)和验证(Validation)

数据模型是通过 Python 类型提示来定义的类,这些类在实例化时会自动使用Python类型注解进行数据验证;确保传入的数据类型符合预期,并且可以自动转换数据类型(比如从字符串到整数)

Pydantic主要用于数据解析、数据校验以及设置管理(如环境变量的读取和验证)。


pydantic 核心功能

  1. 数据验证:Pydantic 可以确保输入数据符合预定义的模型要求。例如,如果定义了一个模型,其中某个字段是整数类型,并且必须大于零,Pydantic 会在接收到这个字段的数据时进行验证,确保数据满足这些条件。如果数据不满足要求,Pydantic 会引发一个错误。
  2. 数据转换:Pydantic 可以自动将输入数据转换为模型所需的数据类型。例如,如果模型中的一个字段被定义为整数类型,但用户输入了一个字符串形式的数字,Pydantic 会自动将这个字符串转换为整数。
  3. 数据序列化:Pydantic 可以将模型实例转换为字典、JSON 或其他格式的数据,这对于 API 开发、数据存储和传输等场景非常有用。
  4. 依赖注入:Pydantic 支持依赖注入,这意味着你可以在模型定义中指定某些字段的值依赖于其他字段的值。这在处理复杂的数据模型时非常有用。
  5. 类型提示:由于 Pydantic 基于 Python 的类型提示系统,因此它可以与 Python 的类型检查工具(如 mypy、typing)一起使用,以提高代码的可读性和可维护性。
  6. 错误处理:提供清晰、结构化的错误消息,帮助开发者快速定位和修复问题;
  7. 设置管理:从配置文件、环境变量等来源加载和验证设置;

BaseModel 模型

BaseModel 是 Pydantic 库中的一个核心类,用于定义数据模型

通过继承 BaseModel,可以创建具有数据验证、类型检查、序列化和反序列化等功能的模型。

名称描述
dict()返回模型的字典表示
json()返回模型的JSON字符串表示
copy()返回模型的浅拷贝
parse_obj()类方法,用于将已有的字典转换为模型实例
parse_raw()类方法,用于将JSON或其他格式的字符串转换为模型实例
parse_file()类方法,用于将JSON或其他格式的文件内容转换为模型实例
from_orm()类方法,用于从兼容ORM对象创建模型实例。这允许Pydantic模型与ORM模型(如SQLAlchemy)无缝协作,通过将ORM模型实例传递给此方法来创建Pydantic模型实例。
schema()类方法,返回模型的JSON Schema表示
schema_json()类方法,返回模型的JSON Schema的JSON字符串表示
construct()类方法,用于创建模型实例,不执行验证
fields一个字典,包含模型的所有字段信息。这可以用于反射或程序化地检查模型定义的字段。
config一个内嵌的类,用于配置模型的行为,如是否允许任意字段、JSON序列化时的行为等。通过在模型定义中创建一个Config子类来自定义这些设置。

Field 类

Field 类是 Pydantic 中的一个辅助类,用于在定义模型字段时提供更细致的控制和额外的配置

它允许开发者对字段的验证、转换、描述等行为进行更精确的定制。

用途

Field 类的主要用途是增强 Pydantic 模型字段的灵活性和可配置性。

通过 Field,你可以:

  • 指定字段的默认值
  • 定制字段的验证规则
  • 定义字段的描述信息
  • 控制字段的转换行为

参数和含义

Field 类接受多个参数,用于定义字段的不同属性。

以下是 Field 的一些主要参数及其含义:

  • alias: 字段的别名,用于在序列化和反序列化时映射不同的名称。
  • default: 字段的默认值。
  • description: 字段的描述信息,通常用于文档生成。
  • ge: 字段的最小值,用于数值类型,表示“大于等于”。
  • gt: 字段的最小值,用于数值类型,表示“大于”。
  • le: 字段的最大值,用于数值类型,表示“小于等于”。
  • lt: 字段的最大值,用于数值类型,表示“小于”。
  • max_length: 字段的最大长度,用于字符串和集合类型。
  • min_length: 字段的最小长度,用于字符串和集合类型。
  • multiple_of: 字段必须是该值的倍数,用于数值类型。
  • title: 字段的标题,通常用于文档生成。
  • max_items: 字段的最大项目数,用于列表和元组类型。
  • min_items: 字段的最小项目数,用于列表和元组类型。
  • exclude: 用于指定在序列化时排除该字段。
  • description_key: 在从字典创建模型实例时,用于查找描述信息的键。
  • allow_mutation: 允许在实例创建后修改该字段的值。

Config 类

pydanticConfig 类是用于配置 pydantic 模型行为的辅助类

通过在模型内部定义 Config 类,你可以指定一系列的设置来影响模型的序列化、反序列化、验证等过程

以下是一些 Config 类中常见的属性及其含义:

属性取值类型默认值含义
allow_mutationboolFalse如果设置为 True,则允许在模型实例化后修改其属性。这通常是不建议的
arbitrary_types_allowedboolFalse如果设置为 True,则允许在模型中使用任意类型(即不是 typing 模块中定义的类型)。这通常是不推荐的;
extrastrforbid控制如何处理模型中未定义的额外字段,forbid禁止额外的字段、allow允许额外的字段,但不会包括在序列化或反序列化中;merge允许额外的字段,并且在序列化或反序列化时会包括它们;
validate_assignmentboolTrue如果设置为 True,则当为模型属性赋值时进行验证。这通常用于确保模型的状态始终有效。
validate_allboolTrue如果设置为 True,则对模型中的所有字段进行验证,即使某些字段没有被明确设置。
json_encodersDict[Type, Callable]{}用于指定自定义的 JSON 解码器。键是字符串,表示 JSON 中的键名或类型标识,值是用于将这些值解码为 Python 对象的函数。
env_prefixstr‘’用于从环境变量中加载配置的前缀。例如,如果设置为 'APP_',那么 APP_NAME 环境变量将被用于加载 name 字段的值
env_filestrNone指定一个环境变量文件的路径,该文件包含以点分隔的键值对,用于加载配置
env_file_encodingstr‘utf-8’指定环境变量文件的编码格式
alias_generatorCallable[[str], str]None用于生成字段的别名。这可以用于在序列化或反序列化时更改字段名。
use_enum_valuesboolTrue如果设置为 True,则使用枚举值进行序列化和反序列化,而不是枚举成员的名称
from_attributesboolFalse从类的属性中自动创建模型字段
orm_modeboolFalse指定是否启用ORM模式,用于与ORM库(如SQLAlchemy)集成

基础类型验证

UserModel 是一个基于 Pydantic 的数据模型,有两个字段:name(字符串类型)和 age(整数类型);

当尝试用非整数类型的值(如字符串 “thirty”)初始化 age 字段时,Pydantic 会抛出 ValidationError

from pydantic import BaseModel, ValidationError
from datetime import date


class StudentModel(BaseModel):
    sid: int    # 学生学号,必填字段(无默认值的时候为必填字段)
    name: str   # 学生姓名
    age: int    # 学生年龄
    gender: str = "男"   # 学生性别,有默认值,选填字段
    clz: str = "S95001"  # 学生所属班级
    city: str       # 学生所属城市
    phone: str      # 学生手机号码
    birthday: date  # 学生生日


# 创建一条符合StudentModel模型的学生数据
# sid字段定义为int类型,但允许传递可转为int的str类型数据,如"1001"
# sbirthday字段定义为date类型,但允许传递可以转为date的str类型数据,如"1999-11-20"
student_info1 = {
    "sid": 1001,
    "name": "张三",
    "age": 18,
    "city": "上海",
    "phone": "10086",
    "birthday": "1999-11-20",
}

student1 = StudentModel(**student_info1)
print(type(student1), student1)  # <class '__main__.StudentModel'> no=1001 name='张三' gender=True birthday=datetime.date(1999, 11, 20) clsz='S95001'

print(student1.sid)     # 1001
print(student1.name)    # 张三
print(student1.age)     # 18
print(student1.gender)  # 男
print(student1.clz)     # S95001
print(student1.city)    # 上海
print(student1.phone)   # 10086
print(student1.birthday)# 1999-11-20

print(student1.dict())  # {'sid': 1001, 'name': '张三', 'age': 18, 'gender': '男', 'clz': 'S95001', 'city': '上海', 'phone': '10086', 'birthday': datetime.date(1999, 11, 20)}
print(student1.json())  # {"sid":1001,"name":"张三","age":18,"gender":"男","clz":"S95001","city":"上海","phone":"10086","birthday":"1999-11-20"}

# 创建一条不符合StudentModel模型的学生数据
# age字段传递非int类型且无法转换为int的数据
student_info2 = {
    "sid": "1002",
    "name": "李四",
    "age": "thirty",
    "city": "北京",
    "phone": "10086",
    "birthday": "1998-07-21"
}
try:
    student2 = StudentModel(**student_info2)
except ValidationError as e:
    print(e.json())  # 异常信息支持转换为json格式

# 这里会抛出错误,因为 age 不是 int 类型,错误信息如下:
# [
#     {
#         "type": "int_parsing",
#         "loc": ["sage"],
#         "msg": "Input should be a valid integer, unable to parse string as an integer", "input": "thirty",
#         "url": "https://errors.pydantic.dev/2.6/v/int_parsing"
#     }
# ]

必填字段验证

from datetime import date
from typing import Optional
from pydantic import BaseModel, Field, ValidationError


class StudentModel(BaseModel):
    sid: int    # 学生学号,必填字段(无默认值的时候为必填字段)
    name: str   # 学生姓名
    age: int    # 学生年龄
    gender: str = "男"   # 学生性别,有默认值,选填字段
    clz: str = "S95001"  # 学生所属班级
    # 利用typing模块进行类型注解,仅是提示作用,没有强制,可以为None,但必须传递
    city: Optional[str]  # 学生所属城市
    # 利用Field辅助类进行类型注解和验证,默认为None,可以不传递
    phone: str = Field(None, description="手机号码")    # 学生手机号码
    birthday: date  # 学生生日


# 创建一条符合StudentModel模型的学生数据
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    city="上海",
    phone="10086",
    birthday="1999-11-20"
)
print(type(student1), student1) # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='10086' birthday=datetime.date(1999, 11, 20)


# 不传递phone字段
student2 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    city="北京",
    birthday="1999-11-20"
)
print(type(student2), student2)  # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='北京' phone=None birthday=datetime.date(1999, 11, 20)


# 不传递city字段
try:
    student3 = StudentModel(
        sid=1001,
        name="张三",
        age=18,
        birthday="1999-11-20"
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['city']

长度验证

from datetime import date
from typing import Optional
from pydantic import BaseModel, Field, ValidationError


class StudentModel(BaseModel):
    sid: int    # 学生学号,必填字段(无默认值的时候为必填字段)
    name: str = Field(..., min_length=2, max_length=10, description="学生姓名(字符不可小于2或大于10)")
    age: int    # 学生年龄
    gender: str = "男"   # 学生性别,有默认值,选填字段
    clz: str = "S95001"  # 学生所属班级
    city: Optional[str]  # 学生所属城市
    phone: str = Field(None, min_length=11, max_length=11, description="手机号码(字符仅可为11)")
    birthday: date  # 学生生日


# 创建一条符合StudentModel模型的学生数据
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    city="上海",
    phone="13500001234",
    birthday="1999-11-20"
)
print(type(student1), student1) # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20)


# name字段长度小于2
try:
    student2 = StudentModel(
        sid=1001,
        name="张",
        age=18,
        city="上海",
        phone="13500001234",
        birthday="1999-11-20"
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['name']


# phone字段长度非11
try:
    student3 = StudentModel(
        sid=1001,
        name="张三",
        age=18,
        city="上海",
        phone="10086",
        birthday="1999-11-20"
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['phone']

大小验证

from datetime import date
from typing import Optional
from pydantic import BaseModel, Field, ValidationError


class StudentModel(BaseModel):
    sid: int = Field(..., gt=1000, lt=9999, description="学生学号(不可小于1000或大于9999)")
    name: str = Field(..., min_length=2, max_length=10, description="学生姓名(字符不可小于2或大于10)")
    age: int = Field(..., gt=0, lt=100, description="学生年龄(不可小于0或大于100)")
    gender: str = "男"   # 学生性别,有默认值,选填字段
    clz: str = "S95001"  # 学生所属班级
    city: Optional[str]  # 学生所属城市
    phone: str = Field(None, min_length=11, max_length=11, description="手机号码(字符仅可为11)")
    birthday: date  # 学生生日


# 创建一条符合StudentModel模型的学生数据
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    city="上海",
    phone="13500001234",
    birthday="1999-11-20"
)
print(type(student1), student1) # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20)


# sid字段小于1000
try:
    student2 = StudentModel(
        sid=50,
        name="张三",
        age=18,
        city="上海",
        phone="13500001234",
        birthday="1999-11-20"
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['sid']


# age字段大于100
try:
    student3 = StudentModel(
        sid=1001,
        name="张三",
        age=101,
        city="上海",
        phone="13500001234",
        birthday="1999-11-20"
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['age']

枚举值验证

from datetime import date
from typing import Optional
from pydantic import BaseModel, Field, ValidationError
from enum import Enum


class Gender(Enum):
    MALE = '男'
    FEMALE = '女'


class StudentModel(BaseModel):
    sid: int = Field(..., gt=1000, lt=9999, description="学生学号(不可小于1000或大于9999)")
    name: str = Field(..., min_length=2, max_length=10, description="学生姓名(字符不可小于2或大于10)")
    age: int = Field(..., gt=0, lt=100, description="学生年龄(不可小于0或大于100)")
    gender: Gender = Field(default="男", description="学生性别(仅可为男或女)")
    clz: str = "S95001"  # 学生所属班级
    city: Optional[str]  # 学生所属城市
    phone: str = Field(None, min_length=11, max_length=11, description="手机号码(字符仅可为11)")
    birthday: date  # 学生生日


# 创建一条符合StudentModel模型的学生数据
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    gender="男",
    city="上海",
    phone="13500001234",
    birthday="1999-11-20"
)
print(type(student1), student1) # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20)


# gender字段非男或女
try:
    student2 = StudentModel(
        sid=1001,
        name="张三",
        age=18,
        gender="不男不女",
        city="上海",
        phone="13500001234",
        birthday="1999-11-20"
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['gender']

容器验证

from datetime import date
from typing import Optional, List
from pydantic import BaseModel, Field, ValidationError
from enum import Enum


class Gender(Enum):
    MALE = '男'
    FEMALE = '女'


class StudentModel(BaseModel):
    sid: int = Field(..., gt=1000, lt=9999, description="学生学号(不可小于1000或大于9999)")
    name: str = Field(..., min_length=2, max_length=10, description="学生姓名(字符不可小于2或大于10)")
    age: int = Field(..., gt=0, lt=100, description="学生年龄(不可小于0或大于100)")
    gender: Gender = Field(default="男", description="学生性别(仅可为男或女)")
    clz: str = "S95001"  # 学生所属班级
    city: Optional[str]  # 学生所属城市
    phone: str = Field(None, min_length=11, max_length=11, description="手机号码(字符仅可为11)")
    birthday: date  # 学生生日
    hobby: List[str] = Field(default=None, min_items=1, max_items=3, description="学生爱好(可以为None或不可小于1或大于3个爱好)")


# 创建一条符合StudentModel模型的学生数据
# 不传递hobby字段
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    gender="男",
    city="上海",
    phone="13500001234",
    birthday="1999-11-20",
)
print(type(student1), student1) # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20)


# 创建一条符合StudentModel模型的学生数据
# 传递hobby字段,参数项为1个
student2 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    gender="男",
    city="上海",
    phone="13500001234",
    birthday="1999-11-20",
    hobby=["篮球"]
)
print(type(student2), student2) # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender=<Gender.MALE: '男'> clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20) hobby=['篮球']


# 传递hobby字段,参数项为大于3个
try:
    student3 = StudentModel(
        sid=1001,
        name="张三",
        age=18,
        city="上海",
        phone="13500001234",
        birthday="1999-11-20",
        hobby=["篮球", "电竞", "直播", "户外", "汽车", "美食"]
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['hobby']

自定义验证器

from datetime import date
from typing import Optional, List
from pydantic import BaseModel, Field, ValidationError, validator
from enum import Enum


class Gender(Enum):
    MALE = '男'
    FEMALE = '女'


class StudentModel(BaseModel):
    sid: int = Field(..., gt=1000, lt=9999, description="学生学号(不可小于1000或大于9999)")
    name: str = Field(..., min_length=2, max_length=10, description="学生姓名(字符不可小于2或大于10)")
    age: int = Field(..., gt=0, lt=100, description="学生年龄(不可小于0或大于100)")
    gender: Gender = Field(default="男", description="学生性别(仅可为男或女)")
    clz: str = "S95001"  # 学生所属班级
    city: Optional[str]  # 学生所属城市
    phone: str = Field(None, min_length=11, max_length=11, description="手机号码(字符仅可为11)")
    birthday: date  # 学生生日
    hobby: List[str] = Field(default=None, min_items=1, max_items=3,
                             description="学生爱好(可以为None或不可小于1或大于3个爱好)")

    @validator('clz')
    def check_clz_startswith(cls, v):
        if not v.startswith("S950"):
            raise ValueError('班级编号必须以S950开头')
        return v

    @validator("hobby")
    def check_hobby_items(cls, hobby, values):
        if values["gender"] == Gender.MALE and hobby[0] != "篮球":
            raise ValueError("本班级男同学的第一爱好必须是篮球哦")
        return hobby


# 创建一条符合StudentModel模型的学生数据
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    city="上海",
    phone="13500001234",
    birthday="1999-11-20",
)
print(type(student1), student1)  # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20) hobby=None


# clz字段,不以S950开头
try:
    student2 = StudentModel(
        sid=1001,
        name="张三",
        age=18,
        clz="10086",
        city="上海",
        phone="13500001234",
        birthday="1999-11-20",
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['hobby']


# gender为男的同学,hobby中不传递篮球或篮球不在第一元素
try:
    student3 = StudentModel(
        sid=1001,
        name="张三",
        age=18,
        gender="男",
        city="上海",
        phone="13500001234",
        birthday="1999-11-20",
        hobby=["电竞", "直播", "户外"]
    )
except ValidationError as e:
    # 将错误信息去除首位符号,转换为字典类型,方便取出错误信息
    error_msg = eval(e.json()[1:-1])
    print("发送错误的字段:", error_msg["loc"]) # 发送错误的字段: ['hobby']

嵌套模型

from typing import List
from pydantic import BaseModel


class Student(BaseModel):
    num: str
    name: str


class Teacher(BaseModel):
    num: str
    name: str
    domain: str


class Clazz(BaseModel):
    num: str
    maximum: int


class School(BaseModel):
    clazz: List[Clazz]
    student: List[Student]
    teacher: List[Teacher]


student1 = Student(num="S1001", name="张三")
student2 = Student(num="S1002", name="李四")
student3 = Student(num="S1003", name="王五")

teacher1 = Teacher(num="T1001", name="赵德柱", domain="数学")
teacher2 = Teacher(num="T1001", name="王铁锤", domain="语文")
teacher3 = Teacher(num="T1001", name="吴鱼子", domain="英语")

clazz1 = Clazz(num="C1001", maximum=30)
clazz2 = Clazz(num="C1001", maximum=40)

school = School(
    clazz=[clazz1, clazz2],
    student=[student1, student2, student3],
    teacher=[teacher1, teacher2, teacher3]
)
print(type(school), school)  # <class '__main__.School'> clazz=[Clazz(num='C1001', maximum=30), Clazz(num='C1001', maximum=40)] student=[Student(num='S1001', name='张三'), Student(num='S1002', name='李四'), Student(num='S1003', name='王五')] teacher=[Teacher(num='T1001', name='赵德柱', domain='数学'), Teacher(num='T1001', name='王铁锤', domain='语文'), Teacher(num='T1001', name='吴鱼子', domain='英语')]

print(school.dict())  # {'clazz': [{'num': 'C1001', 'maximum': 30}, {'num': 'C1001', 'maximum': 40}], 'student': [{'num': 'S1001', 'name': '张三'}, {'num': 'S1002', 'name': '李四'}, {'num': 'S1003', 'name': '王五'}], 'teacher': [{'num': 'T1001', 'name': '赵德柱', 'domain': '数学'}, {'num': 'T1001', 'name': '王铁锤', 'domain': '语文'}, {'num': 'T1001', 'name': '吴鱼子', 'domain': '英语'}]}

print(school.json())  # {"clazz":[{"num":"C1001","maximum":30},{"num":"C1001","maximum":40}],"student":[{"num":"S1001","name":"张三"},{"num":"S1002","name":"李四"},{"num":"S1003","name":"王五"}],"teacher":[{"num":"T1001","name":"赵德柱","domain":"数学"},{"num":"T1001","name":"王铁锤","domain":"语文"},{"num":"T1001","name":"吴鱼子","domain":"英语"}]}

JSON 数据导入

from pathlib import Path
from datetime import date
from typing import Optional, List
from pydantic import BaseModel, Field, ValidationError, validator
from enum import Enum


class Gender(Enum):
    MALE = '男'
    FEMALE = '女'


class StudentModel(BaseModel):
    sid: int = Field(..., gt=1000, lt=9999, description="学生学号(不可小于1000或大于9999)")
    name: str = Field(..., min_length=2, max_length=10, description="学生姓名(字符不可小于2或大于10)")
    age: int = Field(..., gt=0, lt=100, description="学生年龄(不可小于0或大于100)")
    gender: Gender = Field(default="男", description="学生性别(仅可为男或女)")
    clz: str = "S95001"  # 学生所属班级
    city: Optional[str]  # 学生所属城市
    phone: str = Field(None, min_length=11, max_length=11, description="手机号码(字符仅可为11)")
    birthday: date  # 学生生日
    hobby: List[str] = Field(default=None, min_items=1, max_items=3,
                             description="学生爱好(可以为None或不可小于1或大于3个爱好)")

    @validator('clz')
    def check_clz_startswith(cls, v):
        if not v.startswith("S950"):
            raise ValueError('班级编号必须以S950开头')
        return v

    @validator("hobby")
    def check_hobby_items(cls, hobby, values):
        if values["gender"] == Gender.MALE and hobby[0] != "篮球":
            raise ValueError("本班级男同学的第一爱好必须是篮球哦")
        return hobby


# 创建一条符合StudentModel模型的学生数据
student1 = StudentModel(
    sid=1001,
    name="张三",
    age=18,
    city="上海",
    phone="13500001234",
    birthday="1999-11-20",
)
print(type(student1), student1)  # <class '__main__.StudentModel'> sid=1001 name='张三' age=18 gender='男' clz='S95001' city='上海' phone='13500001234' birthday=datetime.date(1999, 11, 20) hobby=None

# 从指定路径下的json文件中读取内容,通过BaseModel模型的parse_file函数进行解析
path = Path("./example.json")
# example.json文件数据如下:
# {
#   "sid": 1101,
#   "name": "弗兰加圣香克斯",
#   "age": 35,
#   "gender": "男",
#   "clz": "S95001",
#   "city": "北海",
#   "phone": "13500001234",
#   "birthday": "1985-11-20",
#   "hobby": [
#     "篮球",
#     "美食",
#     "户外"
#   ]
# }

file_data = StudentModel.parse_file(path)
print(file_data.name)      # 弗兰加圣香克斯
print(file_data.dict())    # {'sid': 1101, 'name': '弗兰加圣香克斯', 'age': 35, 'gender': <Gender.MALE: '男'>, 'clz': 'S95001', 'city': '北海', 'phone': '13500001234', 'birthday': datetime.date(1985, 11, 20), 'hobby': ['篮球', '美食', '户外']}
print(file_data.json())    # {"sid":1101,"name":"弗兰加圣香克斯","age":35,"gender":"男","clz":"S95001","city":"北海","phone":"13500001234","birthday":"1985-11-20","hobby":["篮球","美食","户外"]}


ORM 数据导入

from typing import List

from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel, constr

Base = declarative_base()

# 创建一张表(ORM模型)
class CompanyOrm(Base):
    __tablename__ = 'companies'
    id = Column(Integer, primary_key=True, nullable=False)
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    name = Column(String(63), unique=True)
    domains = Column(ARRAY(String(255)))


# 创建一个pydantic的BaseModel模型
class CompanyModel(BaseModel):
    id: int
    public_key: constr(max_length=20)
    name: constr(max_length=63)
    domains: List[constr(max_length=255)]

    class Config:
        orm_mode = True
        from_attributes = True


# 实例化一个数据库表(ORM模型)对象
co_orm = CompanyOrm(
    id=123,
    public_key='foobar',
    name='Testing',
    domains=['example.com', 'foobar.com'],
)
print(type(co_orm), co_orm) # <class '__main__.CompanyOrm'> <__main__.CompanyOrm object at 0x7fbfd848f580>


# 使用BaseModel类调用from_orm函数,将CompanyOrm模型解析成CompanyModel模型
company_orm = CompanyModel.from_orm(co_orm)
print(type(company_orm), company_orm)   # <class '__main__.CompanyModel'> id=123 public_key='foobar' name='Testing' domains=['example.com', 'foobar.com']

print(company_orm.dict())   # {'id': 123, 'public_key': 'foobar', 'name': 'Testing', 'domains': ['example.com', 'foobar.com']}
print(company_orm.json())   # {"id":123,"public_key":"foobar","name":"Testing","domains":["example.com","foobar.com"]}


总结

pydantic 模块的核心是 BaseModel 类
它让我们能够轻松定义数据模型,并在数据转换、验证和序列化过程中获得强大的功能。
通过类型注解,我们可以为模型字段指定数据类型,并确保输入数据满足这些要求。
如果数据不符合预期,pydantic 会引发错误,帮助我们及时发现并处理问题。
此外,pydantic 还提供了依赖注入、文档生成等高级功能,进一步丰富了数据模型的应用场景。
无论是API开发、数据存储,还是其他数据处理任务,pydantic 都能帮助我们提高代码质量、减少错误,并提升开发效率。

  • 48
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

需要休息的KK.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值