惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
G
Google Developers Blog
J
Java Code Geeks
The GitHub Blog
The GitHub Blog
F
Full Disclosure
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Vercel News
Vercel News
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Announcements
Recent Announcements
Help Net Security
Help Net Security
The Hacker News
The Hacker News
IT之家
IT之家
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
V
Visual Studio Blog
博客园 - 聂微东
Hacker News: Ask HN
Hacker News: Ask HN
H
Hacker News: Front Page
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
Security Archives - TechRepublic
Security Archives - TechRepublic
Simon Willison's Weblog
Simon Willison's Weblog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Troy Hunt's Blog
Last Week in AI
Last Week in AI
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
博客园 - 【当耐特】
有赞技术团队
有赞技术团队
AWS News Blog
AWS News Blog
Blog — PlanetScale
Blog — PlanetScale
博客园_首页
Google DeepMind News
Google DeepMind News
Cloudbric
Cloudbric
N
News | PayPal Newsroom
A
About on SuperTechFans
S
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
雷峰网
雷峰网
T
The Exploit Database - CXSecurity.com
罗磊的独立博客
K
Kaspersky official blog
The Cloudflare Blog
I
Intezer

博客园 - 一名程序媛呀

Anki插件开发必知必会:钩子函数与右键菜单定制 httpx 传参总报错?这次把 GET、POST、文件上传到响应处理的坑给你一次填平 从卡顿到丝滑:FastAPI 调用外部 API 的正确姿势(httpx 实战) 还在 XHR、Fetch 和 Axios 之间纠结?我踩过的坑,希望你一个都不用碰到 你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南 Uvicorn、Gunicorn 傻傻分不清?FastAPI 生产部署避坑指南 Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招! 刚部署的 LibreTranslate 频频翻车?我掏出了 20 年前的 StarDict 词典,用 FastAPI 搭了个本地词典翻译 API 别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了 掏出手机就能搭个 WebDAV 同步服务器?这操作有点香 别只盯着GitBook了!这个文档神器让你的笔记秒变网站 写爬虫时用了代理还被封?Python 代理的那些隐藏坑,我替你踩明白了 FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好 旧手机别扔!用 Termux 搭个私人云盘,比网盘香多了 你的FastAPI又在服务器上“跑不起来”了?来,今天咱把打包这件事彻底聊透 写页面时别再把 Element Plus 整个搬进来啦!Vue3按需加载的坑我帮你踩平了 前端包管理咋选?我从npm叛逃到pnpm的血泪史(附避坑指南) 聊聊 fetch 使用中我踩过的那些坑和正确打开方式 FastApiAdmin 后端接口开发好了,前端管理界面怎么调用与显示? 给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍 我用了FastApiAdmin后,连夜把踩过的坑都整理出来了 告别 Typora 后的新欢:我把所有笔记迁移到了 Obsidian 这个“第二大脑” 你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线 让 FastAPI Agent 思考不阻塞:手把手教你实现异步任务与后台处理方案 让FastAPI Agent真正记住你:聊聊会话记忆与持久化存储的落地实践 FastAPI Agent 函数调用实战:我让 AI 学会了“自己动手查天气“ 初探:用 FastAPI 搭建你的第一个 AI Agent 接口 FastAPI 少有人提的实用技巧:把 Depends 依赖提到路由层,代码少写60% FastAPI 生产环境静态文件完全指南:从 /favicon.ico 404 到 HSTS 混合内容,一次全根治 用了loguru我才明白,Python日志还能这么写 FastAPI 后台任务:BackgroundTasks 的使用场景与注意事项 FastAPI配置管理避坑指南:从硬编码到 .env 与 pydantic_settings 类,连路由用法都给你捋清楚 FastAPI 文件上传避坑全指南:分块存盘、类型校验与安全兜底 FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透 FastAPI订单防超卖实战:从数据库锁到Saga分布式事务,这一篇给你理清了 FastAPI 生产环境避坑指南:用 Alembic 管理数据库迁移,别再手动改表结构了! FastAPI服务半夜又挂了?先别急着重启,查查你的数据库连接池“池子”是不是漏了 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 Vue 3 组合式 API 香是香,但从Vue2迁移时你可别像我当初一样踩进这 3 个深坑里 我用fastapi-scaff搭了个项目,两天工期缩到两小时,老板以为我开挂了 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! FastAPI自动生成的API文档太丑?我花了一晚上把它改成了客户愿意付费的样子 告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战 FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南” FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案 FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了! FastAPI + Celery 实战:异步任务的坑与解法,我帮你踩了一遍 FastAPI子应用挂载:别再让root_path坑你一夜 FastAPI项目半夜报警吵醒你?聊聊告警这事儿怎么搞! 别再数据线了!用FastAPI 5分钟搭个局域网文件+剪贴板神器 FastAPI单元测试实战:别等上线被喷才后悔,TestClient用对了真香! FastAPI状态共享秘籍:别再让中间件、依赖和路由“各自为政”了! FastAPI实战:WebSocket vs Socket.IO,这回真给我整明白了! 从0到1,FastAPI + PostgreSQL + Tortoise ORM 实战避坑指南 FastAPI + PostgreSQL 实战:给应用装上“缓存”和“日志”翅膀 FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透
FastAPI + Pydantic 模型终极实战手册:从能跑就行到固若金汤,这些技巧你一定用得上
一名程序媛呀 · 2026-04-26 · via 博客园 - 一名程序媛呀

💥 先说案例

你有没有遇到过这种情况:接口调得好好的,前端突然跟你说字段对不上,你检查了半天才发现——原来是返回的字段名跟数据库不一样,手动在代码里东拼西凑转换,最后把自己绕晕了。

刚用 FastAPI 写项目时,可能我们的 Pydantic 模型写得跟阉割版一样:所有字段全设成可选,校验基本靠吼,序列化靠手搓字典。

或许要到到一次线上问题,如因为手机号格式没拦住,脏数据把报表全搞乱,才会老老实实把 Pydantic 好好研究一遍。

今天这篇咱们就来聊聊这中间的坑、每天都在用的模型配置技巧等。不堆参数,只讲最实用的那几招。

🎯 这篇文章能帮你搞定什么

用最少的时间学会定义稳固的 BaseModel,分清 必填 可选 字段,用 别名 搞定前后端字段不一致的尴尬。

再深入一点,用 field_validator 做好自定义校验,最后彻底搞清楚 model_dump()model_dump_json() 的区别。

这些可能就是让你明天上班直接用的东西。

📌 先搭地基:字段类型与默认值

咱们先把模型理解成一份严谨的合同。定义好字段类型和默认值,FastAPI 才能自动生成精准的请求体文档,并在数据进来那一刻就帮你拦住不靠谱的参数。

举个最基础的用户注册模型:

from pydantic import BaseModel

class UserRegister(BaseModel):
    username: str
    password: str
    age: int = 18

你可能会问:这样简单,还用讲?重点在后面。这里 age 有个默认值 18,意味着不传也不会报错,而 username password 不传就直接 422。

🧩 必填 vs 可选:别再满屏 Optional 了

早期想着偷懒😅,简单一点,所有非必填字段都写成 Optional[str] = None ,看着没问题,但有一次没注意,一个应该必填的昵称字段没传,代码也没挂,新用户顶着空昵称到处跑。

所以记住一个原则:真正可选的字段才用 Optional 或 Union[..., None],并且给个 None 默认值。如果业务上必须传,就老老实实只写类型

from typing import Optional

class UserProfile(BaseModel):
    nickname: str
    bio: Optional[str] = None

这样做的额外好处是:FastAPI 自动生成的文档里,哪个字段必填一目了然,前端小伙伴再也不会跑来问你“这个要不要传”。

🏷️ 别名与数据转换:字段名打架的终结者

接下来说个最容易翻车的点:
后端模型习惯用 snake_case 下划线命名法,比如 user_name ,但前端传来的 JSON 里全是 camelCase 驼峰命名的 userName

这里要记得,别手动改 dict,用 Pydantic 的 alias 一招搞定。

from pydantic import BaseModel, Field

class User(BaseModel):
    user_name: str = Field(..., alias="userName")

还有一点要注意,用了 alias 却在返回数据时忘了序列化配置。好在 Pydantic v2 里,调用 model_dump(by_alias=True) 就能优雅地在输出时保持别名,这时前端才算彻底舒服了。

🔍 深度点:自定义校验怎么玩

是不是以为加上类型就万事大吉了?别太天真。
用户传个 phone="123" ,类型检查不会报错,但这却不是有效的手机号。怎么办?这时候就该 field_validator 出场了。

官方文档虽然提供了很多示例,但根据以往的经验,把校验逻辑拆成独立方法、加上 @field_validator 装饰器,比塞在模型里写一大堆 lambda 好维护得多。

from pydantic import BaseModel, field_validator
import re

class RegisterForm(BaseModel):
    phone: str
    password: str

    @field_validator("phone")
    @classmethod
    def check_phone(cls, v):
        if not re.match(r"^1[3-9]\d{9}$", v):
            raise ValueError("手机号格式不正确")
        return v

加上这么一段,校验不通过,Pydantic 直接抛出 ValidationError,FastAPI 会帮你转成友好的 422 响应,干净利落,后台再也不会收到乱七八糟的电话号了!

🛡️ 全流程拦截的 model_validator

上面说了 field_validator 管单个字段,有时需要横跨多个字段做联动校验,比如“密码和确认密码是否一致”,这就要请出 model_validator 了。

from pydantic import model_validator

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

    @model_validator(mode="after")
    def check_passwords_match(self):
        if self.password != self.confirm_password:
            raise ValueError("两次密码不一致,好好检查下")
        return self

mode 选 "after" 意思是在各个字段单独校验都通过后再跑,拿到的 self 里字段值已经可靠了。

这种跨字段校验扔在模型里,比在路由函数里写 if 判断要利落得多,还不会忘了校验导致脏数据溜进去。

⚙️ 计算字段 computed_field,别再手动拼属性了

有些字段根本不需要前端传,而是根据其他字段算出来的。
以前我都写个 @property,然后序列化时还得手动拼字典绕一大圈。后来发现 Pydantic 直接提供了 computed_field,丝滑得不行。

from pydantic import BaseModel, computed_field

class Order(BaseModel):
    unit_price: float
    quantity: int

    @computed_field
    @property
    def total_price(self) -> float:
        return self.unit_price * self.quantity

这样 total_price 会像普通字段一样出现在序列化结果里,但模型初始化时根本不用传它。⚠️ 但要注意那个 @property 装饰器不能省,不然 Pydantic 不认。

这个组合用顺手了,代码里少写很多冗余逻辑。

🔄 model_dump() 与 model_dump_json():序列化的分叉口

再说个我曾经犯迷糊的点,以为这俩差不多。其实区别还是很明显的:

model_dump() 返回的是 Python 字典,
model_dump_json() 返回的是 JSON 字符串。

在 FastAPI 的路径函数里直接 return 模型实例即可,框架会自动调用序列化,但假如你要自己处理后返回给前端,或者存到 Redis,那必须搞清用哪个。

一个顺手的小技巧:
如果想在接口响应里把字段转成别名形式,直接 model_dump(by_alias=True),而 model_dump_json 默认就会尊重别名配置,省得手动替换 key。
还有,model_dump 有个参数 exclude_unset=True ,可以只输出显式赋值的字段,在更新接口里格外好用,避免把没改的字段覆盖成默认值。

🎯 模型配置 ConfigDict,你的模型管家

说到这了,再加个知识点:就是用 alias 时那个 model_dump(by_alias=True) 老得手动传,要是忘了咋整?总不能每次都赌自己记性好吧。
这时候就该请出模型的管家—— ConfigDict。直接在模型里定义一套默认行为,省心多了。

from pydantic import BaseModel, ConfigDict

class User(BaseModel):
    model_config = ConfigDict(
        populate_by_name=True, # 允许用字段名或别名填充,容错率拉满(请求体里传 user_name 或者 userName 都认)
        str_strip_whitespace=True, # 自动去掉字符串首尾空格,别再手动 strip 了
        use_enum_values=True, # 枚举字段序列化时直接用值而不是对象
    )
    user_name: str = Field(..., alias="userName")
    age: int

✂️ 序列化时排除字段,exclude 用得巧省大事

再说个经常翻车的场景:
用户登录接口返回用户信息,password_hash 字段绝不能返回给前端。怎么优雅地排除?

三种常用姿势,看情况选:

第一种,直接在模型配置里设 exclude,这样所有用到这模型的地方都不会暴露密码了,适合全局保护。

class UserInDB(BaseModel):
    model_config = ConfigDict(exclude={"password_hash"})
    username: str
    password_hash: str

第二种,调用 model_dump() 时临时排除,适合同一个模型不同场景不同需求的弹性操作。

user = UserInDB(username="xiaohei", password_hash="secret123")
data = user.model_dump(exclude={"password_hash"})

第三种,用 Field(exclude=True) 钉死在这个字段上,一劳永逸。

💡 建议把密码、加密密钥这种敏感字段从源头设成 exclude=True,千万别等上线了再补救。

🌱 嵌套模型与继承,解决模型膨胀

当请求模型、数据库模型、响应模型长得像但又略有不同时,别复制粘贴一堆差不多的类,Pydantic 的继承很好用。

class UserBase(BaseModel):
    username: str
    email: str

class UserCreate(UserBase):
    password: str # 创建时需要密码

class UserResponse(UserBase):
    model_config = ConfigDict(from_attributes=True) # 支持 ORM 对象直接转换
    id: int

这样基础字段定义一次,不同场景只加各自需要的。

其中 from_attributes=True 这个尤其关键,它让你可以直接把 SQLAlchemy 模型对象丢进 Pydantic 并自动转换。没用之前,你可能要手动一条条取属性赋值,那个中滋味……。

🧠 最后啰嗦几句

这套搭配就像给数据加了安检门:
BaseModel 定义形状,Field 管别名和描述,field_validator 把坏东西挡在外面,最后用合适的序列化方法把干净数据送出去。
一旦顺手了,你就再也不想回到那个手动校验的手工作坊时代。

🤝 给前端小伙伴的馈赠,JSON Schema 生成

只要你老老实实定义好了 Pydantic 模型,FastAPI 会自动帮你生成详尽的 JSON Schema 放进 OpenAPI 文档里,字段类型、必填性、示例值、描述全都有。

想让体验更好,可以给每个 Field 加上 description 和 examples。这样不仅文档清晰,配合一些代码生成工具甚至能自动产生前端类型定义。

Pydantic 就像洋葱,一层有一层的惊喜。初期用基础类型定义和可选字段就能跑,但等你把 ConfigDict、校验器、计算字段、嵌套继承这些拎起来之后,模型层才是真正稳固的铠甲。

我的口头禅就是:代码能跑只是起点,能防住奇葩数据才是靠谱

好了,我继续去整理笔记文档了,你先把这几个技巧用起来,有问题随时找我唠。🎯


觉得有用的话,点个「收藏」加关注,下次写接口前翻出来瞄一眼,就不容易踩坑啦,模型忘了怎么写时打开手机扫一眼,比翻文档快十倍。我们下篇见。