知识点思维导图
29 个知识节点
参考资料
Python(33) - 实战——AI 生成一个接口 demo
读完后,你应能完成以下任务:
- 绘制“Python(33) - 实战——AI 生成一个接口 demo / 需求分析:先把需求"翻译"成给 AI 的描述”的关键对象与数据流,解释“正因为你学过,才能把需求拆得这么具体——这就是"会读 AI 代码"的前提:你得先知道正确答案长什么样。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(33) - 实战——AI 生成一个接口 demo / AI 生成的全貌:三个文件,三种角色”设计正常与异常输入,验证“和 Java 第 30 课"Entity 给数据库看、In/Out 给前端看"是同一个道理:数据库模型含 id、created_at 这些前端不该传的字段,还可能含密码等不该返回的字段。”,输出首个偏差位置与回归测试结果。
- 实现“Python(33) - 实战——AI 生成一个接口 demo / 读 models.py:数据库的镜像(≈ Entity)”的最小代码或配置,检验“primary_key=True 等于自增:SQLite/MySQL 下整型主键默认自增,前端新增时不该传 id —— 记住这点,下一层 Pydantic 入参就不能有 id。”,输出命令、结果与 Diff,并说明不适用边界。
前面 14~18 篇把 FastAPI、Pydantic、SQLAlchemy、依赖注入这些"零件"一个个讲透了。这一篇我们换个干活方式:不再逐行手写,而是让 AI 一次性生成一个带数据库的接口,你的角色从"码字员"变成"读懂 + 校验 + 联调"。看完你能独立走完一条真实链路:需求 → 写给 AI 的需求描述 → 读懂它生成的三层代码 → 跑起来联调。
Java 第 30 课是"从下往上一行行盖楼",这一篇是同一个目标的 AI 版本。为什么这样安排?因为 Python 的 AI/胶水定位决定了真实开发里你大概率会让 Copilot / Cursor / Claude 先生成骨架,再自己改。但这恰恰是新手最危险的阶段——AI 生成的代码看着能跑,你却说不清它对不对。这一篇就教你怎么把 AI 当脚手架,同时守住"会读、会改、会验证"的底线。
【前端类比】这就像你用 v0 / Cursor 生成一个 React 组件:它能秒出一个能渲染的 <UserForm />,但你必须看懂它的 props、state、副作用,知道哪里要改、哪里有坑。后端接口同理——AI 生成不等于你可以不懂。
一、需求分析:先把需求"翻译"成给 AI 的描述
【前端类比】这一步就像你接到一个页面需求,先不急着喊 AI 生成,而是先想清楚:要几个接口?每个接口收什么、返回什么?数据长啥样?把这些想明白,AI 才能生成对的代码——你描述得越含糊,AI 越爱自由发挥。
我们要做的需求很常见:文章管理(一个最小博客后台)。基础就是经典 CRUD(增删改查):
| 操作 | 前端动作 | 接口 | HTTP 方法 |
|---|---|---|---|
| 查列表 | 进页面拉文章 | GET /articles |
GET |
| 查详情 | 点某篇看详情 | GET /articles/{id} |
GET |
| 新增 | 写完点发布 | POST /articles |
POST |
| 修改 | 编辑后保存 | PUT /articles/{id} |
PUT |
| 删除 | 点删除 | DELETE /articles/{id} |
DELETE |
注意 URL 风格:这里用 RESTful 风格(资源
/articles+ HTTP 方法表达动作),和一些既有 Java 服务的/article/add、/article/list(动词放路径里)不同。两种都常见,FastAPI 社区更偏 RESTful,跟着团队规范走即可。
一篇文章至少这些字段:
id 主键, 自增
title 标题, 必填
content 正文, 必填
status 状态, 1=已发布 0=草稿, 默认 0
created_at 创建时间, 后端生成
需求清楚后,把它"翻译"成一段能交给 AI 的描述(这就是你给 AI 的 prompt):
用 FastAPI + SQLAlchemy 2.0 写一个文章管理 CRUD 接口,要求:
1. 数据库用 SQLite(本地文件 app.db),文章字段:
id(主键自增)、title(必填)、content(必填)、
status(int, 1已发布/0草稿, 默认0)、created_at(创建时间)
2. 请求体和响应体用 Pydantic 模型,二者分开(新增时前端不传 id/created_at)
3. 数据库 Session 用 Depends 依赖注入按请求创建
4. 提供 增/删/改/查列表/查详情 五个接口,查不到返回 404
5. 代码分文件:models.py(ORM)、schemas.py(Pydantic)、main.py(路由)
这段描述里每一条都对应你前面学过的知识点(Pydantic 见第 15 篇、SQLAlchemy 见第 16 篇、Depends 见第 18 篇)。正因为你学过,才能把需求拆得这么具体——这就是"会读 AI 代码"的前提:你得先知道正确答案长什么样。
二、AI 生成的全貌:三个文件,三种角色
AI 按上面的描述生成了三个文件。先别急着看代码,先看清这张分层图——它和 Java 第 30 课的"从下往上盖楼"是一个意思,只是落到 Python 的文件上:
前端 axios
│ POST /articles { title, content }
▼
┌────────────────────────────────────────────────┐
│ main.py 路由层: 收参数→调DB→return │ ≈ Controller
│ @app.post("/articles") │
└──────────────────────┬───────────────────────────┘
▼
┌────────────────────────────────────────────────┐
│ schemas.py Pydantic: 校验入参 / 塑形出参 │ ≈ In / Out (DTO)
│ ArticleCreate(入参) ArticleOut(出参) │
└──────────────────────┬───────────────────────────┘
▼
┌────────────────────────────────────────────────┐
│ models.py SQLAlchemy: 表↔类映射, 真正存数据 │ ≈ Entity
│ class Article(Base) │
└──────────────────────┬───────────────────────────┘
▼
SQLite articles 表
这里藏着 Python 后端最大的一个新手坑,必须先点破:有两套"模型",名字都叫 Model,但完全是两回事。
SQLAlchemy 模型(models.py) |
Pydantic 模型(schemas.py) |
|
|---|---|---|
| 干啥的 | 映射数据库表,负责存数据 | 校验请求 / 塑形响应,负责接口出入境 |
| 基类 | class Article(Base) |
class ArticleCreate(BaseModel) |
| 对应 Java | Entity (@TableName) |
In / Out (DTO) |
| 前端类比 | Prisma 的 model |
zod schema / TS interface |
⚠️ 为什么要分两套、不能用一套?和 Java 第 30 课"Entity 给数据库看、In/Out 给前端看"是同一个道理:数据库模型含
id、created_at这些前端不该传的字段,还可能含密码等不该返回的字段。混用会导致前端能乱传 id、或后端把敏感字段泄露出去。AI 有时为了省事会只生成一套——这正是你要盯住、要求它拆开的地方。
下面逐个文件读,重点是读懂 + 标出哪里要警惕。
三、读 models.py:数据库的镜像(≈ Entity)
这一层定义"表长什么样",对应 Java 的 Entity。用的是 SQLAlchemy 2.0 写法(详见第 16 篇):
【读这段时你该警惕的点】
primary_key=True等于自增:SQLite/MySQL 下整型主键默认自增,前端新增时不该传 id —— 记住这点,下一层 Pydantic 入参就不能有 id。default=datetime.now(不带括号!):传的是函数本身,让每次插入时才调用取当时时间。AI 偶尔会写成datetime.now()(带括号),那会变成"建表那一刻算出一个固定时间,所有行都用它"——这是个隐蔽 bug,读到这行要停一下确认没括号。echo=True生产要关:会把每条 SQL 打到日志,学习期开着看 SQL 很爽,上线务必关掉。
【前端类比】这层就是你 Prisma 的
schema.prisma:定义 model、字段、主键、默认值。Mapped[int]这种写法看着陌生,把它当成"带类型标注的字段声明"即可,类型注解机制见第 11 篇。
四、读 schemas.py:接口的出入境(≈ In / Out)
这一层是 Pydantic 模型,专门管"前端能传什么、后端返回什么",对应 Java 的 In/Out DTO。这是和 models.py 最容易混的地方,重点读。
【这段的三个关键认知】
- 入参故意"缺字段"是对的,不是 bug:
ArticleCreate没有id/created_at,因为这俩由后端/数据库生成,前端传了也该被忽略。AI 如果在入参里塞了 id,你要删掉。 from_attributes=True是 ORM → Pydantic 的桥(Pydantic v2 的写法;v1 里叫orm_mode = True,AI 有时会写串版本,看到orm_mode就知道它在用旧写法)。有了它,路由里才能return article_对象让 FastAPI 自动把 ORM 对象塑形成ArticleOut。- 对照前端:
ArticleCreate≈ 你和后端约定的interface CreateArticleReq,ArticleOut≈interface ArticleResp。区别是——Pydantic 这俩是运行时真实存在、真的会校验的对象,不像 TS interface 编译后就没了(第 15 篇反复强调过这个差异)。
| 入参 ArticleCreate | 出参 ArticleOut | ORM Article | |
|---|---|---|---|
| 给谁看 | 前端→后端 | 后端→前端 | 后端↔数据库 |
| 含 id 吗 | ❌ 不含 | ✅ 含 | ✅ 含 |
| 前端类比 | 请求体 type | 响应 type | Prisma model |
五、读 main.py:路由层(≈ Controller)
最后是把上面两层串起来的路由层,对应 Java 的 Controller:收参数 → 操作数据库 → return,本身不该堆业务逻辑。
并排看一眼 Express,路由壳子几乎一样,差异都在第 14 篇讲过(装饰器即路由、参数靠签名、return 即响应):
// Express 对照: 同样五个路由, 但参数从 req 掏、要手写校验、返回手动 res.json
app.post('/articles', (req, res) => {
const { title, content } = req.body // 手动取, 还得自己校验 title 是不是 string
// ...自己拼 SQL 或调 ORM, 自己处理 404...
res.status(201).json(article)
})
【这层最该盯住的点】
response_model=ArticleOut是安全阀:它强制响应只输出ArticleOut声明的字段。就算你不小心return了带敏感字段的 ORM 对象,多余字段也会被过滤掉。AI 若漏写这个参数,接口能跑但会"裸返回"整个 ORM 对象——要补上。add/commit两步别漏 commit:SQLAlchemy 不是 Prisma,add只是暂存,忘了commit()数据根本没存进库(第 16 篇的核心坑)。AI 一般不会漏,但你 review 时要扫一眼每个写操作后面有没有 commit。model_dump()是 Pydantic v2 写法(v1 叫.dict())。看到 AI 写.dict()说明它在用旧 API,能跑但已过时。
六、跑起来 + 联调
代码读懂了,跑起来验证。FastAPI 自己不带服务器,靠 uvicorn 跑(见第 13、14 篇):
# 装依赖: fastapi 框架 + uvicorn 服务器 + sqlalchemy ORM
pip install "fastapi[standard]" uvicorn sqlalchemy
# 启动: main 是文件名(main.py), app 是实例名, --reload 类似前端的 nodemon 热重载
uvicorn main:app --reload
启动后,先别急着写前端,打开 http://127.0.0.1:8000/docs——FastAPI 白送的可交互 Swagger 文档(Express 要装 swagger 才有)。五个接口、参数、请求体结构全在上面,能直接点 "Try it out" 发请求。这是验证 AI 代码最快的方式:不用写一行前端,先在 /docs 把五个接口点一遍。
也可以用 curl 走一遍完整链路:
# 新增一篇(POST, body 是 JSON)
curl -X POST http://127.0.0.1:8000/articles \
-H "Content-Type: application/json" \
-d '{"title":"第一篇","content":"正文内容","status":1}'
# 返回 {"id":1,"title":"第一篇","content":"正文内容","status":1,"created_at":"2026-06-11T..."}
# 查列表
curl http://127.0.0.1:8000/articles
# 查不存在的 → 验证 404 分支
curl http://127.0.0.1:8000/articles/999
# 返回 {"detail":"文章不存在"}, HTTP 状态码 404
前端联调就回到你的主场,和调任何 RESTful 接口一样:
import request from '@/utils/request' // 项目封装的 axios 实例
// 查列表 —— GET, 无参数
export function fetchArticles() {
return request({ url: '/articles', method: 'get' })
}
// 新增 —— POST, 参数放 data(作为 JSON 请求体)
export function createArticle(payload: {
title: string
content: string
status?: number
}) {
return request({ url: '/articles', method: 'post', data: payload })
}
【联调自查清单】出问题按这个顺序排查(和 Java 第 30 课同源,但响应约定不同):
后端收不到参数 / 422 ?
├─ POST/PUT → 前端是否用了 data(请求体), 而不是 params(URL查询串)?
├─ 字段名/类型对不对? title 传了 null、status 传了字符串 → Pydantic 直接 422
└─ 看响应里的 detail, FastAPI 会精确告诉你哪个字段不合法
404 ?
└─ 路径 id 对应的数据不存在, 命中了 raise HTTPException(404) 分支
500 ?
├─ 多半是后端代码异常: 忘了 commit、Session 用法错、default=datetime.now() 带括号等
└─ 看 uvicorn 终端打印的异常栈追根因(echo=True 时还能看到执行了哪些 SQL)
⚠️ 响应约定差异:FastAPI 默认直接用 HTTP 状态码表达结果(200 成功 / 422 校验失败 / 404 找不到),返回体就是数据本身或
{"detail": "..."}。这和一些既有 Java 服务“一律 HTTP 200,靠业务错误码区分成败”是两种风格。前端对接 FastAPI 时看 HTTP 状态码 + catch 错误即可,不用先判断业务错误码。具体用哪套看团队约定。
七、用 AI 生成接口的正确姿势(本篇的真正重点)
代码会读了,回到这一篇的核心命题:怎么把 AI 当好脚手架,又不被它坑。沉淀成一份 review 清单,AI 生成后逐条过:
| 检查项 | 为什么 | 怎么改 |
|---|---|---|
| 入参/出参模型分开了吗 | 混用会让前端能传 id、或泄露敏感字段 | 拆成 XxxCreate / XxxOut 两个模型 |
| 入参里有没有混进 id/created_at | 这些该由后端生成 | 从入参模型删掉 |
出参有没有配 response_model |
没配会裸返回整个 ORM 对象 | 路由装饰器补 response_model= |
每个写操作后有没有 commit() |
漏了数据不落库 | add/delete/改属性后补 commit |
default=datetime.now 带括号没 |
带括号会让所有行用同一时间 | 去掉括号, 传函数本身 |
| 用的是 v2 还是 v1 API | .dict()/orm_mode 是过时写法 |
统一到 .model_dump()/from_attributes |
| Session 是按请求建的吗 | 全局共享非线程安全 | 用 Depends(get_db) |
| 查不到有没有处理 | 静默返回 None 会误导前端 | raise HTTPException(404) |
一句话总结这套方法:AI 负责出初稿和体力活,你负责出判断。能列出这张清单本身,说明你前面 14~18 篇没白学——你不是在"信任 AI",而是在"校验 AI"。这正是从前端转后端、又要用好 AI 编程的人最该练的能力:不被代码量吓住,也不被"能跑"骗过去。
八、总结
- 需求分析:先把需求"翻译"成给 AI 的描述:正因为你学过,才能把需求拆得这么具体——这就是"会读 AI 代码"的前提:你得先知道正确答案长什么样。
- AI 生成的全貌:三个文件,三种角色:| 干啥的 | 映射数据库表,负责存数据 | 校验请求 / 塑形响应,负责接口出入境 |
- 读 models.py:数据库的镜像(≈ Entity):primary_key=True 等于自增:SQLite/MySQL 下整型主键默认自增,前端新增时不该传 id —— 记住这点,下一层 Pydantic 入参就不能有 id。
- 读 schemas.py:接口的出入境(≈ In / Out):入参故意"缺字段"是对的,不是 bug:ArticleCreate 没有 id/created_at,因为这俩由后端/数据库生成,前端传了也该被忽略。 -> from_attributes=True 是 ORM → Pydantic 的桥(Pydantic v2 的写法; -> 对照前端:ArticleCreate ≈ 你和后端约定的 interface CreateArticleReq,ArticleOut ≈ interface ArticleResp。
- 读 main.py:路由层(≈ Controller):response_model=ArticleOut 是安全阀:它强制响应只输出 ArticleOut 声明的字段。
- 用 AI 生成接口的正确姿势(本篇的真正重点):| 用的是 v2 还是 v1 API | .dict()/orm_mode 是过时写法 | 统一到 .model_dump()/from_attributes |
学完自测
选择所有正确答案;提交后逐项核对判断依据。