配置与序列化
本教程共 50 篇 · 第 21 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:学会用 model_config 配置模型行为,用 model_dump、model_dump_json、model_validate 完成对象的导出与重建,并能从 ORM 对象加载数据。
模型不只是「收数据、校验」。实际开发中,你还要把模型变成字典、变成 JSON 字符串,或者反过来从一段数据重建模型。Pydantic v2 把这些能力收敛成几个统一的方法,并集中用 model_config 管理配置。
21-1 什么是序列化
「序列化」就是把内存里的 Python 对象,变成可以传输、存储的格式(比如字典、JSON 字符串)。「反序列化」则相反,把外面来的字典或 JSON 还原成模型对象。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
下面三个方法,就是你日常最常用的「出口」和「入口」。
21-2 model_dump() 导出为字典
model_dump() 把模型对象转成一个普通 Python 字典。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
tax: float | None = None
item = Item(name="键盘", price=199.0, tax=0.1)
data = item.model_dump()
print(data) # {'name': '键盘', 'price': 199.0, 'tax': 0.1}
print(type(data)) # <class 'dict'>
转成字典后,你就能把它喂给数据库驱动、日志组件,或自己再加工。注意这是 v2 写法,老的 .dict() 已经废弃,不要再用。
Note严禁使用 Pydantic v1 的
.dict()与.json()。v2 对应物是model_dump()和model_dump_json()。
21-3 model_dump_json() 导出为 JSON 字符串
如果你要直接返回 JSON 文本(比如给前端、写入文件),用 model_dump_json(),一步到位拿到字符串。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
item = Item(name="鼠标", price=99.0)
json_str = item.model_dump_json()
print(json_str) # {"name":"鼠标","price":99.0}
print(type(json_str)) # <class 'str'>
这两个 dump 方法都接受实用参数:exclude={"tax"} 排除某些字段,include={"name"} 只保留指定字段,exclude_none=True 跳过值为 None 的字段。
item = Item(name="键盘", price=199.0, tax=None)
print(item.model_dump(exclude_none=True)) # {'name': '键盘', 'price': 199.0}
21-4 model_validate() 从字典重建模型
model_validate() 是 model_dump() 的反操作:丢给它一个字典,它按模型规则校验并构造对象。
from pydantic import BaseModel, ValidationError
class Item(BaseModel):
name: str
price: float
raw = {"name": "显示器", "price": 899.0}
item = Item.model_validate(raw)
print(item.name) # 显示器
如果字典不合法(比如价格传成字符串且转不了),它会抛出 ValidationError,和直接用 Item(...) 构造的行为一致。
Tip从外部来源(数据库行、第三方接口)拿到的是普通字典时用
model_validate();如果你已经确定数据可信、想跳过校验提速,才考虑更底层的model_construct()。初学阶段老实用model_validate()。
21-5 model_config 统一配置模型
Pydantic v2 把所有模型级配置收进 model_config,它是一个类属性。旧版那种 class Config: 的写法已经废弃,不要再写。
from pydantic import BaseModel, ConfigDict
class Item(BaseModel):
model_config = ConfigDict(
title="商品模型",
extra="forbid",
)
name: str
price: float
ConfigDict 里能放很多开关,下面挑最常用的讲。
21-6 from_attributes:从 ORM 对象加载
这是最常用的一项。数据库 ORM(如 SQLAlchemy)取出的对象,属性存在 .name、.price 这种属性访问上,而不是字典的键。开启 from_attributes=True 后,model_validate() 就能直接从这种对象读数据。
from pydantic import BaseModel, ConfigDict
class PostResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
title: str
# 假设这是 SQLAlchemy 查出来的对象,不是字典
class PostORM:
def __init__(self, id: int, title: str) -> None:
self.id = id
self.title = title
orm_obj = PostORM(id=1, title="我的第一篇文章")
post = PostResponse.model_validate(orm_obj)
print(post.title) # 我的第一篇文章
Notev1 里这叫
orm_mode = True,写在class Config中。v2 改名为from_attributes=True,写在model_config。这是迁移时最容易踩的坑,记住新写法。
21-7 几个实用配置项
extra="forbid":模型里没声明的字段,传进来就报错。可防止多余数据悄悄混进来。extra="ignore":忽略多余字段(默认行为)。str_strip_whitespace=True:自动去掉字符串首尾空格。str_to_lower=True:把所有字符串转小写。frozen=True:把模型变成「只读」,创建后不能改属性,类似不可变对象。
from pydantic import BaseModel, ConfigDict
class User(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
name: str
age: int
u = User(name=" 小红 ", age=18)
print(repr(u.name)) # '小红'(首尾空格已去掉)
21-8 在 FastAPI 里串起 ORM 与响应
真实流程是这样的:数据库取出 ORM 对象 → model_validate 转成响应模型 → FastAPI 自动序列化返回。配合 response_model 还能自动过滤字段。
from fastapi import FastAPI
from pydantic import BaseModel, ConfigDict
from typing import Any
class PostORM:
def __init__(self, id: int, title: str, secret: str) -> None:
self.id = id
self.title = title
self.secret = secret
class PostResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
title: str
app = FastAPI()
@app.get("/posts/{post_id}")
async def get_post(post_id: int) -> dict[str, Any]:
orm_obj = PostORM(id=post_id, title="标题", secret="不能外泄")
return PostResponse.model_validate(orm_obj).model_dump()
secret 字段不在 PostResponse 里,所以 model_dump() 的结果天然不含它——这就实现了「数据库字段与对外字段分离」。职责上:ORM 管数据库,Pydantic 管 API 契约。
21-9 model_dump 的进阶排除写法
exclude 和 include 还能用嵌套字典语法,精确控制深层字段。比如模型里套了子模型,只想排除子模型的某个字段:
from pydantic import BaseModel
class Address(BaseModel):
city: str
secret_code: str
class User(BaseModel):
name: str
address: Address
u = User(name="小李", address=Address(city="北京", secret_code="X9"))
# 只排除 address 里的 secret_code
print(u.model_dump(exclude={"address": {"secret_code"}}))
# {'name': '小李', 'address': {'city': '北京'}}
这种「字典里套字典」的写法,键对应字段名,值为 True 或再嵌一层,非常灵活。
21-10 model_copy 拷贝并改值
想基于已有对象生成一个新对象(比如改一个字段),用 model_copy(),比重新 model_validate 省事。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
item = Item(name="键盘", price=199.0)
new_item = item.model_copy(update={"price": 219.0})
print(new_item.price) # 219.0
print(item.price) # 199.0(原对象没变)
update 传一个字典,只会覆盖你指定的字段,其余原样保留。它返回的是全新对象,不动原来的。
要注意 model_copy(update=...) 默认不会重新跑校验,传进去的值会被直接写入字段。也就是说,你若把 price 改成一个字符串,它不会像构造模型时那样报错。想让新值也经过完整校验,可以先 model_dump() 拿到字典、合并改动后再用 model_validate() 重建。另外 model_copy() 默认是浅拷贝,嵌套的子模型仍与原对象共享同一份数据,需要彻底独立时要显式传 deep=True。
21-11 从 Pydantic v1 迁移的要点
如果你接手的是老项目,很可能会看到 v1 写法。对照着改,关键有这几处:把 class Config 改成 model_config = ConfigDict(...);把 orm_mode = True 改成 from_attributes=True;把 .dict() 改成 model_dump()、.json() 改成 model_dump_json();把 @validator 改成 @field_validator(并加 @classmethod);把 @root_validator 改成 @model_validator。官方还提供了一个 bump-pydantic 工具,能自动批量改写大部分常规代码,改完跑一遍测试即可。最新版 FastAPI 已经要求必须用 Pydantic v2,老旧的 pydantic.v1 兼容子模块也不再被新 Python 支持,所以迁移是绕不开的一步。
Note迁移时不要混用:一个 v2 模型的字段,不能再把 v1 模型当子字段。要么全是 v2,要么分开放在两个独立模型里。
21-12 小结
本章你掌握了 Pydantic v2 的配置与序列化:model_dump() 出字典、model_dump_json() 出 JSON 串、model_validate() 从字典重建,三者是 v2 的标准三件套;model_config = ConfigDict(...) 统一管配置,其中 from_attributes=True 让你能直接从 ORM 对象加载数据,配合 model_copy 还能安全派生新对象。再次强调,所有写法都是 v2——没有 .dict()、没有 class Config、没有 orm_mode,老项目记得按本章最后一节逐条迁移。