@seclink: 分享新文章 《基于 fastapi + SQLAlchemy ORM 的应用,有必要修改为基于 fastapi + sqlmodel 吗?》 https://juejin.cn/post/7648902852933304360…

X AI KOLs Following 工具

摘要

该文章对比了FastAPI+SQLAlchemy与SQLModel的使用场景,分析了迁移的收益与成本,建议存量项目不必全量迁移。

分享新文章 《基于 fastapi + SQLAlchemy ORM 的应用,有必要修改为基于 fastapi + sqlmodel 吗?》 https://juejin.cn/post/7648902852933304360…
查看原文
查看缓存全文

缓存时间: 2026/06/09 14:54

分享新文章 《基于 fastapi + SQLAlchemy ORM 的应用,有必要修改为基于 fastapi + sqlmodel 吗?》 https://juejin.cn/post/7648902852933304360…


基于 fastapi + SQLAlchemy ORM 的应用,有必要修改为基于 fastapi + sqlmodel 吗?

Source: https://juejin.cn/post/7648902852933304360 在 Python Web 开发(尤其是 FastAPI + SQLAlchemy 生态)中,ModelsSchemas是两个经常被提及但职责完全不同的概念。

简单来说:Models 负责“如何把数据存在数据库”,而 Schemas 负责“如何对 API 输入输出的数据进行校验和过滤”。


一、 核心概念对比

维度Models (数据库模型 / 实体)Schemas (校验模型 / DTO)主要定位****数据持久化(Database Persistence)****数据传输与边界校验(Validation & DTO)****代表框架SQLAlchemy, Tortoise-ORM, Django ORMPydantic, Marshmallow存在区域数据库的**表结构(Table Schema)**映射。API 接口的输入参数(Request)和输出数据(Response)核心职责- 映射表名、主键、外键、索引

  • 处理数据库关联关系 (Relationship)

  • 执行 SQL 读写

  • 验证字段类型、长度、格式(如 Email、手机号)

  • 过滤敏感数据(如隐藏密码字段)

  • 序列化为 JSON / 反序列化


二、 用一个生动的比喻来理解

假设你在开一家高端私人会所(系统):

  • Models 是“会所内部的员工档案”:- 里面记录了员工的身份证号、真实姓名、家庭住址、合同编号、银行卡号(相当于数据库中的所有敏感和物理字段)。 - 这个档案只有经理(数据库/ORM)能看,绝对不能展示给客人。
  • Schemas 是“员工的工作胸牌”:- 胸牌上只写着:艺名(Nick Name)、工作岗位(只暴露客人需要看到的信息)。 - 另外,客人要入会时,需要填一张“入会申请表”(输入 Schema),胸牌制作前需要检查格式对不对(数据校验)。

三、 数据流向图(它们在系统中的位置)

在一次完整的 API 请求中,它们各自站岗在不同的关口:

📥 [客户端请求 (JSON)]
              │
              ▼
   ┌──────────────────────┐
   │ Schemas (Pydantic)   │ <--- 1. 拦截并校验数据(如:密码是否够长?Email 格式对吗?)
   └──────────────────────┘
              │ (验证通过,转换为 Python 对象)
              ▼
   ┌──────────────────────┐
   │ Service / Controller │ <--- 2. 业务处理
   └──────────────────────┘
              │ (转换为 DB Model 对象)
              ▼
   ┌──────────────────────┐
   │ Models (SQLAlchemy)  │ <--- 3. 映射为数据库表的一行,写入数据库
   └──────────────────────┘
              │
              ▼
       💾 [数据库 (DB)]

四、 代码实例对比

1. Models 的写法 (以 SQLAlchemy 为例)

它关注的是底层数据库的细节。

# app/models/user.py
from sqlalchemy import Column, Integer, String, Boolean
from app.database import Base

class DBUser(Base):
    __tablename__ = "users"  # 物理表名
    
    id = Column(Integer, primary_key=True, index=True) # 数据库主键
    email = Column(String, unique=True, index=True, nullable=False)
    hashed_password = Column(String, nullable=False) # 数据库存的是哈希密文
    is_active = Column(Boolean, default=True)

2. Schemas 的写法 (以 Pydantic 为例)

它关注的是 API 的输入和输出。

# app/schemas/user.py
from pydantic import BaseModel, EmailStr, Field

# 客户端注册时发送的数据(输入 Schema)
class UserCreate(BaseModel):
    email: EmailStr  # 自动验证必须是合法的邮箱格式
    password: str = Field(min_length=8)  # 强制要求密码至少 8 位

# 返回给客户端的数据(输出 Schema / DTO)
class UserResponse(BaseModel):
    id: int
    email: EmailStr
    # 🚫 注意:这里绝对不能写 password 或 hashed_password
    # 这样可以确保敏感的密码信息永远不会通过 API 泄漏给前端

五、 为什么 SQLModel 能把两者合二为一?

因为在传统开发中,DBUserUserResponse存在大量的字段重复(比如idemail),修改一个字段要改两个文件。

SQLModel巧妙地通过 Python 的多重继承,让一个类同时扮演这两个角色:

  • 当它带上table=True时,它是Model(生成数据库表)。
  • 当它不带table=True时,它是Schema(进行 API 验证和过滤)。

对于一个已经使用FastAPI + SQLAlchemy ORM稳定运行的存量项目,通常没有绝对的必要进行全量重构去升级为 SQLModel

尽管 SQLModel 带来了极佳的开发体验,但全量迁移的重构成本和潜在风险往往会大于它带来的直接收益。

以下是关于迁移收益成本/风险以及决策建议的深度对比分析:


一、 迁移至 SQLModel 的核心收益(Why do it?)

如果选择迁移,你将获得以下三个最显著的“爽点”:

1. 消除双重定义,代码量减半(DRY - Don’t Repeat Yourself)

  • 现状 (SQLAlchemy):新增一个表字段,你需要修改models\.py(SQLAlchemy)、schemas\.py(UserCreate)、schemas\.py(UserResponse),很容易漏改。
  • SQLModel:你只需要在BaseModel中加一行,数据库迁移和 API 的输入输出校验将自动同步生效。

2. 完美的 IDE 自动补全与类型安全(Developer Experience)

  • 现状 (SQLAlchemy):执行db\.execute\(select\(User\)\)拿到的是Result,必须通过\.scalars\(\)\.all\(\)转换,且 IDE (VSCode/PyCharm) 经常无法推导出返回列表里是User实体,导致写后续代码时没有属性提示。
  • SQLModeldb\.exec\(select\(User\)\)\.all\(\)会被 IDE 精准识别为list\[User\],从查询到字段访问,全流程补全体验极佳。

3. 与 FastAPI 的原生无缝契合

  • SQLModel 是由 FastAPI 作者 Tiangolo 亲自开发的。SQLModel 实例可以直接当做 FastAPI 的response\_model返回,或者直接作为 Request Body 接收,省去了大量BaseModel\.model\_validate\(db\_obj\)的转换步骤。

二、 迁移的成本与潜在风险(The Cost & Risk)

1. 高级 ORM 特性的适配风险

SQLModel 底层虽然就是 SQLAlchemy,但它对一些复杂的 SQLAlchemy 高级特性进行了封装。如果你的项目大量使用了以下特性,迁移时会遇到不少阻力:

  • 多表继承(如Joined Table InheritancePolymorphic Queries)。
  • 复杂的复合主键/外键关联
  • 高度自定义的relationship属性(如自定义的 association proxy,或非标准的 join 条件)。
  • 在这些场景下,SQLModel 可能会出现类型系统冲突,或者迫使你不得不退化到写sa\_column=Column\(\.\.\.\),从而失去了 SQLModel 原有的简洁性。

2. 存量代码的工作量与测试回归

  • 修改表定义:所有继承declarative\_base\(\)的模型必须重构为继承SQLModel\(table=True\),所有的Column字段要改写为Field
  • 修改查询语句:虽然旧的 SQLAlchemy 查询(db\.execute\(\.\.\.\))在 SQLModel 依然兼容,但为了类型安全,你可能需要将它们逐步改写为db\.exec\(\.\.\.\)
  • Alembic 迁移脚本:已经生成的数据库迁移脚本(Migrations)在模型基类变更后,需要小心处理,避免 Alembic 误判为表被删除重建。

三、 决策矩阵:怎么选?

项目状态建议决策理由与落地策略新建项目 / 微服务🚀果断使用 SQLModel没有任何历史包袱,能够完全享受 SQLModel 带来的极速开发体验。中小型存量项目 (模型数 < 15 个,表结构简单)⚖️可以考虑迁移重构时间在 1-2 天内可控,能够彻底消除冗余代码,提升后续维护效率。中大型企业级项目 (表结构复杂,高度依赖 SQL 优化)🛑维持 SQLAlchemy 现状****安全第一。SQLAlchemy 的生态更成熟,工业级排坑经验多。重构引入的回归 Bug 风险远大于 SQLModel 带来的语法糖收益。希望局部优化🛠️混合使用 (不冲突)**推荐。因为 SQLModel 底层是 SQLAlchemy,它的Session兼容普通的 SQLAlchemy Model。你可以在现有项目中,对新开发的模块**使用 SQLModel,存量模块保持不变,逐步过渡。### 💡 最终建议

  • 如果当前项目运行稳定,且没有频繁的模型变更需求不要修改
  • 如果你们正饱受“API DTO 与 数据库 Model 字段不同步”以及“IDE 没有 SQL 提示”的折磨,可以先挑选一个非核心的子业务模块进行SQLModel 局部重构,验证效果后再决定是否全面铺开。

将**底层的原生驱动(psycopg2)高层 ORM(SQLAlchemy / SQLModel)进行对比,本质上是在“极致性能与精细控制”“开发效率与工程可维护性”**之间做权衡。

以下是两者的优缺点深度对比:


相比于 直接用 psycopg2 , 使用 fastapi + SQLAlchemy ORM 或者 fastapi + sqlmodel 有什么优缺点 ?

一、 直接使用psycopg2(原生驱动)

psycopg2是 PostgreSQL 的底层 C 语言驱动,直接执行原生 SQL。

优点:

  1. 极致的性能(Zero Overhead):没有 ORM 框架在中间做“SQL 转换”和“对象实例化(Row -> Python Object)”的开销。性能几乎达到 Python 操作 PostgreSQL 的物理极限。
  2. 完全掌控 SQL:你可以写任意复杂的 PostgreSQL 特有语法(如窗口函数、递归 CTE、JSONB 操作、全文检索等),不用去研究 ORM 对应的怪异语法。
  3. 更轻量:项目依赖少,启动速度快,没有复杂的 ORM 状态管理(如 Session 状态、延迟加载等带来的坑)。

缺点:

  1. 开发效率极其低下:简单的增删改查(CRUD)也必须手写完整的 SQL 语句,并手动处理占位符(如%s)以防 SQL 注入。
  2. 缺乏类型安全与自动补全:SQL 是以字符串(string)形式写在代码里的,IDE(如 VSCode/PyCharm)无法对 SQL 字符串内的字段做语法检查、自动补全或重构。
  3. 手动结果集转换(ResultSet Mapping)psycopg2默认返回的是元组列表(list\[tuple\])或字典(dict)。你必须自己写代码把它们解析并映射成 Python 对象或 JSON,极易出错。
  4. 同步阻塞(与 FastAPI 冲突)psycopg2同步阻塞的驱动。在 FastAPI 的异步事件循环中,如果直接执行慢查询,会阻塞整个服务。虽然可以通过多线程或换用psycopg3/asyncpg解决,但增加了开发复杂度。

二、 使用FastAPI \+ SQLAlchemy / SQLModel(高层 ORM)

优点:

  1. 极高的开发效率:提供面向对象的操作方式。例如创建一条记录只需db\.add\(user\);查询只需select\(User\),无需编写繁琐的 INSERT/UPDATE 语句。
  2. 自动数据校验与序列化(配合 Pydantic):- 输入:FastAPI 自动将前端 JSON 校验并转换为 Pydantic/SQLModel 对象。 - 输出:直接返回 ORM 对象,FastAPI 会自动过滤掉敏感字段(如密码)并序列化为 JSON。
  3. 无缝支持异步(Async/Await):SQLAlchemy (v1.4+) 和 SQLModel 完美支持异步上下文(配合asyncpg驱动),与 FastAPI 的异步架构完美契合,不会阻塞事件循环。
  4. 数据库迁移工具(Alembic):可以通过 Python 代码(Models)自动生成数据库表结构迁移脚本(Schema Migrations),版本控制极其简单。
  5. 防御 SQL 注入:ORM 在底层自动使用参数化查询,从源头上消除了大部分 SQL 注入的风险。

缺点:

  1. 性能损耗:由于存在 SQL 生成、结果集对象映射(Object Mapping)、Pydantic 校验等多层包装,在高并发、大吞吐场景下,CPU 和内存开销显著高于psycopg2
  2. N+1 查询陷阱:如果不熟悉 ORM 的“延迟加载(Lazy Loading)”机制,很容易写出在循环中查表的代码,产生大量无谓的数据库连接请求,导致系统骤慢。
  3. 学习曲线陡峭:SQLAlchemy 是一个功能极其庞大、复杂的库。在处理复杂联表、事务隔离级别、连接池调优时,学习和排坑的成本很高。

三、 核心维度对比表格

对比维度psycopg2 (原生 SQL)FastAPI + SQLAlchemy / SQLModel查询性能🥇极快(无中间开销)🥈较慢(受 ORM 映射和校验损耗影响)开发速度(写大量样板 SQL 和数据转换代码)🥇极快(自动 CRUD,自动转换 DTO)异步支持原生不支持(需用线程池或换 asyncpg)🥇原生支持(配合 asyncpg 完美异步)类型提示 / 补全(SQL 只是字符串)🥇完美(IDE 可精准推断模型属性)维护成本(改一个字段名要搜索全局 SQL 字符串)🥇(修改 Model 即可,支持 IDE 重构)防 SQL 注入⚠️需手动参数化(稍有不慎容易写出漏洞)🥇天然防范(框架底层强制参数化)

四、 工程实践建议:怎么选?

在实际的企业级 FastAPI 开发中,我们很少走两个极端,通常会采用混合架构(Hybrid Approach)

  1. 80% 的业务(标准 CRUD / 业务逻辑):使用FastAPI + SQLModel / SQLAlchemy。- 享受自动校验、快速开发、类型补全和 Alembic 自动迁移的好处。
  2. 20% 的业务(高性能 / 报表 / 复杂联表):在 ORM 的 Session 中执行原生 SQL。- SQLAlchemy 允许你直接通过session\.execute\(text\("SELECT \.\.\."\)\)执行原生 SQL,这样既保留了 ORM 的连接池管理,又获得了原生 SQL 的极致性能和灵活性。 - 注:如果只用原生 SQL,在 FastAPI 中建议使用asyncpg代替同步的psycopg2

相似文章

@seclink: 分享 《开发者生态信息差报告(2026-06-05)》,有兴趣的朋友可以阅读。 - 现在有人开始把 openclaw 类项目迁移到 AI 眼镜 和 AI 指环等可穿戴设备了 ... - 机器人与具身智能开源 有很多开源的数据和开源的模型 …

X AI KOLs Following

分享了一份开发者生态信息差报告,内容包括将openclaw类项目迁移到AI眼镜和指环等可穿戴设备、机器人与具身智能的开源数据与模型,以及AI API中转站与路由的开源应用。

@beefnoode: https://x.com/beefnoode/status/2062816409030389909

X AI KOLs Timeline

本文基于a16z关于Salesforce无界面产品的分析,探讨AI Agent时代企业软件的护城河从用户界面迁移到底层数据模型、权限体系和工作流逻辑的趋势,并分析了CRM、ATS、ERP等系统的迁移难度差异。