ThankNeko's Blog ThankNeko's Blog
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)

Hoshinozora

尽人事,听天命。
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)
  • Python笔记

    • 基础知识

    • 并发编程

    • 爬虫笔记

    • 模块笔记

    • 后端笔记

      • Pydantic验证
        • Pydantic介绍
          • 简介
          • 特点
          • 转换
          • 安装
        • Pydantic使用
          • BaseModel
          • 字段约束
          • 额外类型
          • 自定义验证器
          • 序列化方法
          • 模型配置
          • BaseSettings
      • FastAPI介绍
      • FastAPI请求
      • FastAPI响应
      • FastAPI路由
      • FastAPI中间件与依赖
      • Tortoise ORM
      • FastAPI实现用户管理
  • C笔记

  • C++笔记

  • Arduino笔记

  • Web笔记

  • Dev
  • Python笔记
  • 后端笔记
Hoshinozora
2025-11-23
目录

Pydantic验证

# Pydantic介绍

# 简介

Pydantic 是一个基于Python类型提示的数据验证、解析和序列化库。它被广泛用于现代Python项目中,尤其在 FastAPI、Django Ninja等框架中作为核心组件,用于自动校验请求/响应数据。

# 特点

  1. 自动数据验证:确保输入数据符合预期类型和约束。

  2. 类型安全:利用 Python 的 typing 模块,IDE 自动补全、静态检查。

  3. 错误信息友好:返回清晰的验证错误(含字段路径)。

  4. 数据转换:自动将字符串转为 int、datetime 等。

  5. 序列化支持:轻松将模型转为JSON兼容字典。

  6. 高性能:底层用 Rust 编写(v2 起),速度极快。

# 转换

Pydantic在验证数据时,如果传入的数据是字符串,并且声明的数据类型为int、bool、date,则会自动尝试将字符串转换为声明的数据类型。

  • "1" → 1

    需声明类型为int

  • "true" / "True" → True

    需声明类型为bool

  • "2023-01-01" → datetime.date(2023, 1, 1)

    需声明类型为datetime.date

# 安装

# 安装pydantic库
pip install pydantic

# email验证、JSON Schema生成等额外功能需要指定安装
pip install pydantic[email, json]
1
2
3
4
5

# Pydantic使用

# BaseModel

# 介绍

Pydantic的核心概念是BaseModel模型,所有Pydantic模型都继承自pydantic.BaseModel。

# 使用

from typing import Literal, List

from pydantic import BaseModel


# 使用Pydantic数据验证只需要定义类型时继承BaseModel类
class User(BaseModel):
    # 定义属性时需要声明类型提示

    # 字符串类型
    name: str
    # 整数类型
    age: int
    # 逻辑或实现多选类型,此处可为整数、字符串、列表,列表中可为整数、字符串
    phone: int | str | List[int | str]
    # 枚举类型,只能是指定字面值
    gender: Literal["male", "female", "Unknown"]
    # 指定默认值,存在默认值的参数实例化时可以留空
    is_active: bool = True


# 创建实例,创建时会自动进行数据类型验证
user = User(name="Hello", age=18, phone=[13344333223], gender="male")
# 访问属性
print(user.name)
# 通过字典导出类中的数据
print(user.model_dump())

# 验证失败示例,传入错误的数据类型
try:
    User(name=233, age="十八", phone=[13344333223], gender="male")
except Exception as e:
    print(e)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 字段约束

使用 pydantic.Field() 可以给数据添加额外的验证规则。

from pydantic import BaseModel, Field

class User(BaseModel):
    name: str = Field(min_length=2, max_length=7)
    age: int = Field(gt=0, le=300)
    description: str = Field(default="", alias="info", max_length=2000)

# 示例
User(name="Hello", age=18, info="What?")  # 验证通过
User(name="A", age=-1)                    # 验证失败
1
2
3
4
5
6
7
8
9
10

常用约束:

gt, ge, lt, le:数值范围。

min_length, max_length:字符串、列表的长度。

default:指定默认值。

pattern:正则匹配(如pattern=r'^Hello')。

alias:外部字段名,在前后端命名规范不一致时使用。

使用后创建实例时需要使用别名,model_dump导出也会使用别名,但是内部属性访问时使用原名。

...:表示字段没有默认值,为必填配置。

如果 default 和 ... 都不指定,则隐式默认值为 None。

# 额外类型

# 网络与格式

from pydantic import (
    AnyUrl,                     # 任意 URL(支持 mailto:, ftp:// 等)
    HttpUrl,                    # 仅 http/https,最大长度 2083
    HttpsUrl,                   # 仅 https
    FileUrl,                    # file:// 协议
    WebsocketUrl,               # ws:// 或 wss://
    AnyHttpUrl,                 # 同 HttpUrl(别名)
    IPvAnyAddress,              # IPv4 或 IPv6 地址
    IPv4Address,                # 仅 IPv4
    IPv6Address,                # 仅 IPv6
    EmailStr,                   # 邮箱字符串(简单正则验证)
    NameEmail,                  # 带名称的邮箱(如 "Alice <a@example.com>")
    SecretStr,                  # 敏感字符串(repr 时隐藏)
    SecretBytes,                # 敏感字节(repr 时隐藏)
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 日期时间

from pydantic import (
    AwareDatetime,              # 带时区信息的 datetime(拒绝 naive)
    NaiveDatetime,              # 不带时区的 datetime(拒绝 aware)
    PastDatetime,               # 必须是过去的时间
    FutureDatetime,             # 必须是未来的时间
)
1
2
3
4
5
6

# 文件与路径

# 标准库,但Pydantic自动支持
from pathlib import Path
# 验证路径是否存在(需自定义或使用 extra-types)
from pydantic import DirectoryPath, FilePath  
1
2
3
4

# 敏感数据处理

from pydantic import SecretStr, SecretBytes

class Login(BaseModel):
    password: SecretStr  # 敏感数据类型

user = Login(password="123456")
print(user.password)  # SecretStr('**********')
print(user.password.get_secret_value())  # "123456"
1
2
3
4
5
6
7
8

# JSON与动态数据

from pydantic import Json

class Payload(BaseModel):
    data: Json[dict]            # 要求是 JSON 字符串,且解析为dict
    raw: Json                   # 解析为任意JSON(dict/list/str/int...)
1
2
3
4
5

# 扩展类型

需要额外安装:pip install pydantic-extra-types

from pydantic_extra_types import (
    Color,                          # CSS 颜色("red", "#ff0000", "rgb(255,0,0)")
    PaymentCardNumber,              # 信用卡号(带 Luhn 校验)
    PhoneNumber,                    # 电话号码(需 phonenumbers 库)
    CountryAlpha2, CountryAlpha3,   # 国家代码(ISO 3166)
    LanguageAlpha2,                 # 语言代码(ISO 639-1)
)
1
2
3
4
5
6
7

# 自定义验证器

# @field_validator

@field_validator是用于对单个字段进行自定义验证或转换的核心装饰器。

装饰器参数是字段名字符串,可以传入多个字段名,但只是对传入的字段分别进行验证。

验证函数必须是 @classmethod。

接收一个参数 v(字段值)。

必须返回最终值,可以是原值,也可以是转换后的值。

from pydantic import BaseModel, field_validator

class User(BaseModel):
    username: str

    @field_validator('username')
    @classmethod
    def check_username(cls, v):
        if len(v) < 3:
            raise ValueError('Username must be at least 3 characters')
        return v

User(username="ab")  # 验证失败
1
2
3
4
5
6
7
8
9
10
11
12
13

mode 参数,用于控制在哪个阶段验证:

mode 说明
"before" 在 Pydantic 类型转换之前(原始输入值)。
"after" 在类型转换之后(默认)。
"plain" 完全跳过内部验证,只用你的函数(只在@field_validator可用)。

# @model_validator

@model_validator 是用于对整个模型实例进行验证或转换的装饰器。它适用于需要多字段逻辑验证或整体数据修正的场景。

mode="before"

在任何字段验证之前执行,需要接收接收的是原始输入字典,适合预处理输入数据。

函数必须是 @classmethod,必须返回数据字典。

class Point(BaseModel):
    x: float
    y: float

    @model_validator(mode='before')
    @classmethod
    def parse_tuple(cls, data):
        # 如果数据格式并非标准数据,则将传入的数据转换为所需的数据
        if isinstance(data, (list, tuple)) and len(data) == 2:
            return {"x": data[0], "y": data[1]}
        return data

# 支持多种输入格式
Point(x=1, y=2)      # 标准数据
Point([1, 2])        # 自动转换
Point((3.5, -1.2))   # 自动转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

mode="after"

在所有字段验证和赋值完成后执行,可以安全访问所有字段,适合业务逻辑校验(常用)。

函数必须是接收 self 实例的方法,必须返回模型实例,通常是 self 原实例。

from pydantic import BaseModel, model_validator

class Signup(BaseModel):
    password: str
    confirm_password: str

    @model_validator(mode='after')
    def check_passwords_match(self):
        # 如果两次密码不一致则报错
        if self.password != self.confirm_password:
            raise ValueError('Passwords do not match')
        return self

# 测试示例
Signup(password="123456", confirm_password="123456")  # 验证通过
Signup(password="123", confirm_password="456")        # 验证失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 序列化方法

user = User(id=1, name="Alice")

# 转为Dict字典
print(user.model_dump())  
# {'id': 1, 'name': 'Alice', 'age': None, 'is_active': True}

# 转为JSON字符串
print(user.model_dump_json())  
# {"id":1,"name":"Alice","age":null,"is_active":true}

# 排除None值
print(user.model_dump(exclude_none=True))
# {'id': 1, 'name': 'Alice', 'is_active': True}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 模型配置

在BaseModel中可以通过 model_config 字段控制行为。

from pydantic import BaseModel, ConfigDict

# ==================================

# extra='forbid': 禁止额外字段,传入未定义字段会报错

class Model_ExtraForbid(BaseModel):
    model_config = ConfigDict(extra='forbid')
    name: str

Model_ExtraForbid(name="Alice", age=30)  # 验证失败

# ==================================

# extra='allow': 允许额外字段,并可通过model_extra访问

class Model_ExtraAllow(BaseModel):
    model_config = ConfigDict(extra='allow')
    name: str

obj = Model_ExtraAllow(name="Bob", city="NYC")
# 输出: {'city': 'NYC'}
print(obj.model_extra)

# ==================================

# str_strip_whitespace=True: 自动去除字符串首尾空格

class Model_Strip(BaseModel):
    model_config = ConfigDict(str_strip_whitespace=True)
    email: str

user = Model_Strip(email="  alice@example.com  ")
# "alice@example.com"
print(user.email)  

# ==================================

# 4. str_to_lower=True: 字符串自动转小写

class Model_Lower(BaseModel):
    model_config = ConfigDict(str_to_lower=True)
    username: str

user = Model_Lower(username="ALICE")
# "alice"
print(user.username)  

# ==================================

# alias_generator: 自动生成别名(如user_name → userName)
def to_camel(s: str) -> str:
    import re
    return re.sub(r'_([a-z])', lambda m: m.group(1).upper(), s)

class Model_AliasGenerator(BaseModel):
    model_config = ConfigDict(
        alias_generator=to_camel,
        populate_by_name=True  # 允许同时用user_id和userId
    )
    user_id: int
    is_active: bool

# 用别名创建实例
obj = Model_AliasGenerator(userId=1, isActive=True)
# 同时也支持用内部名(populate_by_name=True)
obj = Model_AliasGenerator(user_id=1, is_active=True)

# ==================================

# validate_assignment=True: 赋值时验证
class Model_ValidateAssignment(BaseModel):
    model_config = ConfigDict(validate_assignment=True)
    age: int

user = Model_ValidateAssignment(age=25)
user.age = "三十"  # 赋值时验证失败

# ==================================

# 10. frozen=True: 创建不可变模型,其属性值不可变

class Model_Frozen(BaseModel):
    model_config = ConfigDict(frozen=True)
    id: int

obj = Model_Frozen(id=1)
obj.id = 2  # 验证失败因为是不可变模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

# BaseSettings

# 介绍

BaseSettings 是 Pydantic 用于管理应用程序配置的核心类。让我们的配置类中的字段,也能实现数据验证、数据转换等功能。

# BaseSettings需要额外安装
pip install pydantic-settings

# 如果需要使用.env文件加载配置,则需要额外安装dotenv
pip install python-dotenv
1
2
3
4
5

BaseSettings 的配置加载优先级:显式传参 > 环境变量 > .env 文件 > 字段默认值

# 使用

.env 文件

ADMIN_EMAIL=admin@example.com
DATABASE_URL=postgresql://user:pass@localhost/db
DEBUG=true
1
2
3

.env文件中的配置会覆盖Settings类中的配置。

.env文件一般在本地开发时使用,方便管理开发配置。需要将.env加入.gitignore,以忽略上传到代码库。

settings.py 文件

from pydantic_settings import BaseSettings
from pydantic import Field, ConfigDict

# 配置类需要继承BaseSettings类
class Settings(BaseSettings):
    # 在加载配置时,会自动将字段名转换为大写,作为默认的环境变量名
    # 下面的例子同等于: app_name: str = Field(..., env="APP_NAME")
    app_name: str

    #Field中的env参数可以指定在环境变量或.env文件中读取的环境变量名
    admin_email: str = Field(..., env="EMAIL")
    
    database_url: str = "sqlite:///./test.db"
    debug: bool = False

    # 当model_config中设置了env_file时,Pydantic会调用dotenv库动态加载env配置
    model_config = ConfigDict(
        # 也可以用列表指定多个路径,会按顺序查找
        env_file=".env",  # type: ignore
        env_file_encoding="utf-8"  # type: ignore
    )

# 在实例化配置时,也可以显示传参,优先级最高。
settings = Settings(app_name="Override")

if __name__ == "__main__":
    print(settings.admin_email)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

如果Pycharm提示ConfigDict传入了意外实参,可以在代码行后面添加 # type: ignore。

#Python#模块#Pydantic#数据校验
Redis
FastAPI介绍

← Redis FastAPI介绍→

最近更新
01
Vue路由
12-09
02
FastAPI实现用户管理
11-23
03
Tortoise ORM
11-23
更多文章>
Theme by Vdoing | Copyright © 2022-2026 Hoshinozora | MIT License
湘ICP备2022022820号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式