刚用 SQLAlchemy 搭好用户表,跑了半个月数据。
突然,产品说要加个手机号(phone_number)字段,当你改完模型表(models.py),重启项目,结果服务器报错:
sqlalchemy.exc.OperationalError: no such column: users.phone_number
想删表重建?数据全没了;不改?功能没法上线。
你遇到过么?
别慌!今天我们讨论下 SQLAlchemy 的最佳搭档 ——Alembic 轻量级数据迁移工具。仅三步搞定表结构变更。
程序员 躲不开的表结构变更
不管是做小项目,还是企业级应用,数据库表结构从来都不是一成不变的。
- 刚上线的用户表,少了个手机号字段要补加
- 老字段类型不合适,要把
String改成Text - 业务迭代快,要给订单表加个状态字段
这些需求每天都在发生,当需要调整表结构时,你会惊恐地发现:SQLAlchemy只能建表,不能改表。
这正是所有 SQLAlchemy 用户的日常痛点。
传统方案的致命缺陷
大多数开发者首先想到的是"简单粗暴"的解决方案:
01、删库重跑(自杀式)
这个方法是利用了 SQLAlchemy 的特性。
当数据表不存在时,SQLAlchemy 会根据数据表模型,创建一个新表。
当数据表已存在时,SQLAlchemy 并不会重新创建。
如果使用此方案,所有用户数据灰飞烟灭,生产环境绝对不可行。
02、手动SQL脚本(高风险)
-- 手动执行ALTER TABLE
ALTER TABLE users ADD COLUMN phone_number VARCHAR;
当我们需要修改线上数据库的表结构时,首先,需要走审批流程。
一般,先到你的直属领导,再到 DBA。最后,再发更新邮件,抄送相关人员。
在这个流程中,主要依赖的是人。
你很难保证 DBA 永不犯错、也很难保证与DBA之间的沟通永远顺畅无误、也很难保证所有变更记录准确无误。
Alembic 救场!三步实现无痛数据迁移
Alembic 是专门为 SQLAlchemy 打造的轻量级迁移工具,能帮你在保留数据的前提下,自由修改表结构。
接下来直接上实操教程,新手也能跟着做。
01、安装 + 初始化,两步搞定环境搭建
安装 Alembic
pip install alembic -i https://pypi.tuna.tsinghua.edu.cn/simple
创建迁移环境
在项目根目录执行以下命令,<alembic_name>可以自定义
alembic init <alembic_name>
执行完后,项目根目录会出现1 个alembic.ini配置文件 + 1 个<alembic_name>目录。
02、修改核心配置:让 Alembic 和项目适配
修改alembic.ini,配置数据库连接
打开文件,找到sqlalchemy.url,把项目database.py里的SQLALCHEMY_DATABASE_URL复制过来,如 SQLite 的配置:
sqlalchemy.url = sqlite:///./todosapp.db
修改<alembic_name>/env.py,关联项目模型
# 导入项目的 models.py 文件
import models
# 文件 16 行,删除 if 判断
# if config.config_file_name is not None:
fileConfig(config.config_file_name)
# 文件 22 行,修改 target_metadata 的值
target_metadata = models.Base.metadata
03、编写升级脚本
一切准备就绪,可以开始表结构变更操作了。
创建修订脚本
执行命令生成带唯一 ID 的修订文件,文件会自动保存在<alembic_name>/versions目录下:
alembic revision -m "add phone_number column to users table"
编写升级逻辑
打开生成的修订文件,在upgrade函数里写加字段的代码:
def upgrade() -> None:
"""Upgrade schema."""
op.add_column("users", sa.Column("phone_number", sa.String(), nullable=True))
执行升级命令,把变更同步到数据库(<revision_id>换成生成修订文件中的唯一 ID):
alembic upgrade <revision_id>
再检查下 models.py 文件中的 Users 表,确认 phone_number 字段已经添加
# models.py 文件
class Users(Base):
# ...
phone_number = Column(String)
切回 Swagger UI 页面,刷新页面,找到 GET /todo 接口,执行用户登录。
此时,登录成功。表示,我们的 Users 表中的 phone_number 字段添加成功了。
04、编写降级脚本
如果,加字段后发现程序有 bug,需要回退版本,就在downgrade函数里写删字段的代码:
def downgrade() -> None:
"""Downgrade schema."""
op.drop_column("users", "phone_number")
执行降级命令,回退到上一个版本
alembic downgrade -1
降级后,需要同步删除models.py 文件中,Users 表中的 phone_number 字段,否则会再次触发服务器报错。
再次,切回 Swagger UI 页面,刷新页面,找到 GET /todo 接口,执行用户登录。
此时,登录成功。表示,我们的 Users 表中的 phone_number 字段删除成功了。
企业级最佳实践
- 将修订文件纳入版本控制(如Git),确保所有开发者数据库结构一致。
- 在自动化部署流程中,加入数据库迁移步骤。
- 每次生产环境迁移前自动备份数据库。
Alembic 是现代开发必备技能
在快速迭代的互联网时代,数据库结构变更常态化。
Alembic 不仅是一个工具,更是一种工程化思维:所有变更都应该可追溯、可回退、可协作。
现代开发中除了 Alembic,测试也是很重要的一环。
测试包括:手动测试、单元测试、集成测试。这些测试是什么意思呢?它们又有什么区别呢?我们应该怎么样正确的使用它们呢?
下期,我将掰开了,揉碎了,把它们一次性讲清楚。
想要获取本章完整代码,请在评论区回复 【FastAPI】,代码直接复制就能跑。
关于 FastAPI 的其他疑问
别等着被骂:API上线前,一定要把SQLite换成MySQL,附 FastAPI对接代码
别等被骂才后悔:APP上线前,一定要把SQLite换成PostgreSQL,附 FastAPI对接代码
你的API在裸奔?踩坑8小时,从“越权裸奔”到“权限严控”:FastAPI+JWT+依赖注入,这套方案闭眼抄
你的APP要用户反复登录?密码传来传去?FastAPI+JWT实战,一个令牌全打通,安全与体验兼得,代码直接抄
FastAPI 新手紧急避坑:10分钟搞定用户认证4大坑,代码复制即用
相关内容我都给大家做好了,感兴趣的朋友来「我的主页」找一找,直接就可以看到。
欢迎关注 Gong 众号 「王二哥的技术笔记」,每天分享「Python」、「职场」有趣干货,千万不要错过!