12条Cursor规则让AI助手告别过时FastAPI代码

用Cursor项目规则约束AI,杜绝过时的FastAPI写法。
文章针对AI编程助手倾向生成过时FastAPI代码的问题,提出用Cursor的`.mdc`项目规则按glob范围化加载,主动约束生成行为。作者总结12条约定,覆盖四个层面:路由与Schema层强制Pydantic v2 API、薄异步路由、显式`response_model`与状态码、`XCreate/XUpdate/XRead`命名及`extra="forbid"`;数据库层锁定SQLAlchemy 2.0的`Mapped`/`select()`风格、服务层持有事务、关系默认`lazy="raise"`并显式预加载、Base上设命名约定;错误与配置层要求服务层抛自定义`AppError`而非`HTTPException`、配置统一走缓存的`get_settings()`、禁用f-string拼SQL;测试层使用真实Postgres配合`httpx.AsyncClient`与ASGITransport,每测回滚。规则可手动实现,也可购买现成规则包。
AI编程助手经常写出几年前流行的FastAPI模式:.dict()、orm_mode、session.query()、在服务层深处抛出HTTPException、测试用SQLite。代码能跑,但不是今天该写的代码。Cursor的项目规则能解决大部分问题——每个规则是.cursor/rules/下的一个.mdc文件,通过glob只在编辑匹配文件时加载。作者总结了12条最有效的约定。

路由与Schema:强制Pydantic v2与薄路由
1. 只用Pydantic v2 API。 使用model_validate、model_dump、ConfigDict、field_validator,绝不用.dict()、parse_obj、orm_mode、class Config或v1的validator装饰器。这条规则直接切断AI最常犯的怀旧错误。
2. 路由要薄且异步。 解析请求、调用服务、返回Schema。路由里不写SQL或业务规则。这确保关注点分离,让服务层成为唯一业务逻辑入口。
3. 始终声明response_model(或返回类型注解)和明确的status_code: 创建用201,删除用204。显式声明让API契约清晰,AI不会随意返回默认200。
4. Schema命名为XCreate/XUpdate/XRead。 输入模型用ConfigDict(extra="forbid"),这样偷偷传入的"is_admin": true会被拒绝。更新时用model_dump(exclude_unset=True)应用部分更新。命名约定让AI生成一致的结构。
Pydantic v1 到 v2 的迁移是 FastAPI 生态近年最大的一次破坏性变更。v2 用 Rust 重写了核心校验逻辑,性能提升 5–50 倍,同时把 orm_mode 并入 ConfigDict(from_attributes=True),parse_obj/dict() 更名为 model_validate/model_dump,validator 拆分为 field_validator 与 model_validator。由于绝大多数公开教程和问答仍停留在 v1 时代,AI 模型在补全时极易混用两套 API,导致运行时才暴露的 AttributeError 或静默的类型转换差异。用规则把 API 面锁定在 v2,本质上是把「版本漂移」这类低价值问题从代码评审中彻底剔除。
数据库:锁定SQLAlchemy 2.0风格与显式加载
5. 只用SQLAlchemy 2.0风格。 使用Mapped[...]、mapped_column()、select(),绝不用session.query()或declarative_base()。AI训练数据中大量旧式写法,这条规则强制现代异步风格。
6. 服务层拥有事务,用async with session.begin():。仓储层永不提交。使用async_sessionmaker(expire_on_commit=False)。事务边界清晰,避免隐式提交导致的异步IO问题。
7. 关系默认lazy="raise"。 用selectinload()或joinedload()显式加载,这样异步代码里没有隐藏IO。懒加载在异步环境中是定时炸弹,这条规则提前引爆。
8. 在Base上设置MetaData(naming_convention=...),让Alembic约束名确定。绝不要编辑已应用的迁移。命名约定让数据库迁移可预测,AI不会生成随机约束名。
SQLAlchemy 的 lazy="raise" 是一种防御性加载策略:任何未显式预加载的关系在被访问时立即抛出异常,而不是静默发起一次查询。在同步代码里,懒加载只是带来 N+1 查询的性能问题;但在 asyncio 环境下,属性访问发生在 I/O 循环之外,会触发 MissingGreenlet 错误,且往往在序列化响应时才爆发,排查成本很高。把 lazy="raise" 设为默认,再配合 selectinload()(发一条 IN 查询批量加载)或 joinedload()(用 JOIN 一次取回),可以在开发阶段就把隐式 I/O 逼出来,让「哪些关系被加载」成为显式契约而非运行时惊喜。
错误、配置与安全:统一错误处理与配置入口
9. 服务层抛自己的AppError子类,绝不抛HTTPException。 一个异常处理器将它们映射为{"error": {"code": ..., "message": ...}}。这保持服务层与HTTP解耦,错误格式统一。
10. 设置只通过缓存的get_settings(),基于pydantic-settings。其他地方绝不读os.environ,密钥用SecretStr。配置集中管理,避免散落的环境变量读取。
11. 绝不用f-string构建SQL。 用绑定参数或text(...).bindparams()。这条安全规则防止SQL注入,AI有时为图方便用f-string。
测试:真实Postgres与异步客户端
12. pytest + pytest-asyncio + httpx.AsyncClient(transport=ASGITransport(app=app)),针对真实Postgres,每个测试回滚。每个测试后清理app.dependency_overrides。不用SQLite替代。真实数据库测试捕获更多问题,回滚保持隔离。
ASGITransport 是 httpx 提供的传输层适配器,它把请求直接交给 ASGI 应用对象处理,不经过真实网络端口。这样测试仍走完整的中间件、依赖注入和异常处理链路,却省去了启动 uvicorn 和端口管理的开销,同时保持 AsyncClient 的熟悉接口。之所以坚持真实 Postgres 而非 SQLite,是因为两者在 JSONB、数组类型、ON CONFLICT、事务隔离级别和约束行为上差异显著,用 SQLite 通过测试的代码在线上可能直接失败。每个测试在事务内执行并回滚,则让测试之间互不污染,又避免了反复建库删库的昂贵开销。
如何使用这些规则
把这些规则放入.cursor/rules/,每个领域一个文件,glob匹配你的布局。对于Claude Code,同样的约定可以作为仓库根目录的简短CLAUDE.md。作者还提供了一个包含7个范围化.mdc文件和CLAUDE.md的FastAPI规则包,售价12美元,其中两个文件可免费下载(无需邮箱)。所有约定不购买任何东西也能手动实现。
这些规则的核心思路是:不要依赖AI的默认训练数据,而是用项目规则主动约束生成行为。每条规则都针对一个具体的过时模式,简单直接。你可以从最常出现的错误开始,逐步添加自己的规则。
相关推荐

OpenAI与Shield合作:Astra智能体如何革新IT故障修复
OpenAI与Shield合作推出Astra智能体,可自动登录电脑和遗留系统,实时修复IT故障,比传统模型快35%到40%,仅在需要人类判断时引入人工。

iPhone Duo续航实测:一天一充跑不掉,大屏是耗电元凶
iPhone Duo续航实测:重度使用一天需至少充电一次,6.5小时亮屏约剩30%电量。大屏展开频率是耗电关键,MagSafe电池背夹可救场,长期衰减同样值得关注。

Kimi K3.1 Pro开源发布:2.8万亿参数逼近Opus
Moonshot发布开源MoE模型Kimi K3.1 Pro,2.8万亿总参数、1040亿激活参数,Terminal Bench 4.0仅落后Opus 5.5不到1分,单任务成本约三分之一,API价格减半,开源权重11月4日开放。