Alembic op.get_bind 怎么在迁移脚本中执行参数化 SQL
Alembic 迁移脚本需要把一批旧数据改成新状态时,最稳妥的写法是:在 upgrade() 或 downgrade() 中调用 op.get_bind() 拿到当前 Connection,再用 SQLAlchemy 的 text() 和绑定参数执行 SQL。变量放在参数字典里,SQL 结构保持不变;不要把用户数据拼进字符串,也不要在迁移函数里自行创建、提交或关闭连接。
op.get_bind()返回的是当前迁移上下文正在使用的连接,适合需要读取结果、检查rowcount或执行多次 Core SQL 的场景。text("... :name ...")配合bindparam()或执行时参数字典,可以让 SQL 模板和值分离。- 连接属于 Alembic 的迁移事务;迁移脚本不应自行
commit()、rollback()或close()。 - offline 模式没有真实数据库连接,依赖
Connection.execute()读取结果的逻辑不能直接照搬。
官方资料:https://alembic.sqlalchemy.org/en/latest/api/runtime.html
先说结论:需要 Connection 时才用 op.get_bind
Alembic 的 op.execute() 适合把一个迁移操作交给当前变更上下文,例如执行一条 DDL 或不需要读取结果的 SQL。它的返回值不是给业务逻辑使用的结果集。若迁移需要查询旧值、根据结果决定后续更新,或者需要检查受影响行数,就应该拿到当前连接:
from alembic import op
def upgrade():
# 取得 Alembic 当前迁移上下文中的连接,不另建 Engine。
connection = op.get_bind()
# 后续的 text、select、update 等 SQLAlchemy Core 语句都交给它执行。
result = connection.execute(...)
return result.rowcount
这里的关键不是“换一种写法执行 SQL”,而是获得一个可以继续调用 execute()、读取 Result、保持当前事务语义的 SQLAlchemy Connection。Alembic 官方运行时文档也把 get_bind() 定义为返回当前 bind;在 online 模式下,它就是当前用于发出 SQL 的连接。

为什么不直接把变量拼进 SQL 字符串
迁移里的变量可能来自配置、旧表数据或预先计算的映射。把它们直接拼接到 SQL 中,会让引号、类型转换和特殊字符都变成调用方的责任;当迁移跨数据库方言时,问题更难定位。
SQLAlchemy 的 text() 使用命名占位符,执行时再把值交给连接。需要固定类型时,可以用 bindparam() 明确声明:
from alembic import op
from sqlalchemy import String, bindparam, text
def upgrade():
connection = op.get_bind()
statement = text("""
UPDATE account
SET display_name = :new_name
WHERE tenant_id = :tenant_id
AND display_name = :old_name
""").bindparams(
# 显式类型让不同数据库驱动按预期处理字符串参数。
bindparam("new_name", type_=String(200)),
bindparam("old_name", type_=String(200)),
bindparam("tenant_id"),
)
# SQL 模板和值分开传递,避免拼接用户数据或旧表数据。
result = connection.execute(statement, {
"tenant_id": 7,
"old_name": "未命名",
"new_name": "待完善",
})
# rowcount 只作为本次语句的影响行数,不代表整批迁移已经提交。
if result.rowcount == 0:
raise RuntimeError("没有找到需要迁移的账号")
bindparam() 不是把值写回 SQL 字符串,而是让语句携带一个待执行时填充的参数定义。执行时的字典键必须和占位符名称一致;缺少某个键通常会在执行阶段报参数缺失错误。

需要读取数据时,让 Result 留在当前迁移步骤里
数据迁移常见的第二种情况是先查旧值,再按旧值生成新值。此时 op.execute() 不能替代连接执行,因为你需要使用返回的 Result。可以用 mappings() 让每一行按列名访问:
from alembic import op
from sqlalchemy import text
def upgrade():
connection = op.get_bind()
rows = connection.execute(
text("""
SELECT id, display_name
FROM account
WHERE display_name IS NULL
ORDER BY id
"""),
).mappings().all()
for row in rows:
# 先在 Python 中形成确定的新值,再通过参数绑定写回数据库。
new_name = f"账号-{row['id']}"
connection.execute(
text("""
UPDATE account
SET display_name = :new_name
WHERE id = :account_id
"""),
{"new_name": new_name, "account_id": row["id"]},
)
这个例子故意把“查询”和“更新”放在同一个 Connection 上。这样,迁移逻辑使用的是同一个上下文和事务范围;如果中途抛出异常,是否回滚由 Alembic 的迁移配置和外层调用决定,而不是由循环里的某一次 execute() 单独决定。
对旧写法的影响:不要在迁移函数里接管事务
从应用代码迁移到 Alembic 时,最容易带过来的习惯是自己创建 Engine、开启新的事务、执行后立即提交。迁移脚本不应该这样做。op.get_bind() 的价值恰恰在于复用 Alembic 已经配置好的连接和事务环境:
| 做法 | 适用性 | 原因 |
|---|---|---|
op.execute(text(...)) | 不需要读取结果的单条 SQL | 由当前变更上下文执行,代码短 |
op.get_bind() + connection.execute() | 需要参数、结果集、rowcount 或多次 SQL | 直接使用当前 Connection |
迁移中重新 create_engine() | 通常不推荐 | 可能绕开 Alembic 的连接、事务和配置 |
迁移中手动 commit() / close() | 不要这样做 | 会破坏外层迁移的事务与资源管理 |
如果只需要执行一次静态 SQL,继续用 op.execute() 更容易读;如果需要 Connection 的能力,再使用 op.get_bind()。不要为了“看起来更底层”而把所有迁移都改成连接代码。
online 和 offline 模式的边界要提前写清楚
op.get_bind() 依赖已经配置好的迁移上下文;官方文档说明,在 online 模式下它返回真正的数据库连接。生成离线 SQL 时,Alembic 主要把操作编译成输出文本,并不存在可以查询数据库的实时连接。
因此,依赖结果集的迁移应至少满足下面的判断:
- 只在 online 模式执行需要查询旧数据的分支。
- 需要支持 offline 输出时,把数据变换改成数据库内可编译的静态 SQL,或拆成预先生成数据的迁移步骤。
- 不要把“本地能拿到 Connection”误认为所有 Alembic 执行方式都能拿到连接。
最小的上线前检查是:确认迁移命令使用的模式、目标数据库方言和参数类型;再检查语句的影响范围是否由主键或唯一条件约束。这里的检查是迁移设计的一部分,不是用字符串拼接来绕过参数绑定。
一份可直接套用的决策清单
- 只是执行不读结果的 SQL:优先考虑
op.execute()。 - 需要参数化 DML:用
op.get_bind()获取当前连接,配合text()和参数字典。 - 需要固定类型或复杂参数:用
bindparam()明确声明类型。 - 需要查询后再更新:用同一个连接完成查询和写入,避免在脚本中重新创建 Engine。
- 不要在迁移函数里手动提交、回滚或关闭 Alembic 管理的连接。
- 需要生成 offline SQL 时,重新评估所有依赖实时结果集的代码。
常见问题
op.get_bind() 返回的是 Engine 还是 Connection?
在 Alembic online 模式下,它返回当前用于发出迁移 SQL 的 SQLAlchemy Connection。不要把它当成应用层的全局 Engine,也不要在脚本里关闭它。
可以把参数直接写进 text() 吗?
固定不变的 SQL 结构可以写进 text(),运行时值应通过命名占位符和执行参数传入;需要类型信息时再使用 bindparam()。
rowcount 等于最终提交的行数吗?
不一定。它表示当前语句返回的影响行数,具体语义还受数据库驱动和语句类型影响;它不能代替迁移事务是否成功提交的判断。
offline 模式能不能继续用 op.get_bind?
不能把它当成在线连接使用。需要读取真实数据的逻辑依赖数据库连接,应改成可生成 SQL 的方案,或明确只在 online 模式执行。
把连接获取、参数绑定和事务管理分开看,op.get_bind() 的职责就很清楚:它让迁移脚本进入 Alembic 当前的 SQLAlchemy Connection,而不是让脚本接管数据库生命周期。
Alembic 已有数据库怎么标记当前迁移版本
- 上一篇
- Alembic 已有数据库怎么标记当前迁移版本
- 下一篇
- PyArrow Table 怎么合并多个 ChunkedArray 以便连续读取
-
- 文章 · python教程 | 23分钟前 | CLI · python · Python optparse OptionGroup 命令行帮助
- Python optparse.OptionGroup 怎样把旧 CLI 帮助选项分组
- 116浏览 收藏
-
- 文章 · python教程 | 24分钟前 | 配置文件 · Python 命令行参数 argparse fromfile_prefix_chars 参数文件
- Python argparse.fromfile_prefix_chars 怎么从参数文件读取长命令
- 151浏览 收藏
-
- 文章 · python教程 | 24分钟前 |
- Python Base85 和 Base64 有什么区别,短文本该怎么选
- 365浏览 收藏
-
- 文章 · python教程 | 29分钟前 |
- Python bz2.BZ2Decompressor 怎么按输入需求循环解压数据流
- 250浏览 收藏
-
- 文章 · python教程 | 36分钟前 | 类型转换 · pyarrow 安全转换 整数溢出 compute.cast CastOptions Arrow类型
- PyArrow compute.cast 溢出时怎么选择安全转换策略
- 139浏览 收藏
-
- 文章 · python教程 | 36分钟前 |
- PyArrow write_dataset 怎么限制每个 Parquet 文件的行数
- 444浏览 收藏
-
- 文章 · python教程 | 37分钟前 | python · Python pyarrow table ChunkedArray combine_chunks
- PyArrow Table 怎么合并多个 ChunkedArray 以便连续读取
- 200浏览 收藏
-
- 文章 · python教程 | 47分钟前 | python · 数据库迁移 · Python sqlalchemy Alembic Operations.execute 参数化SQL
- Alembic 迁移脚本怎么执行带参数的 SQL
- 496浏览 收藏
-
- 文章 · python教程 | 51分钟前 | sqlalchemy · Python教程 · Python SQLite 数据库迁移 Alembic batch_alter_table
- Alembic 在 SQLite 中怎么批量修改列约束
- 341浏览 收藏
-
- 文章 · python教程 | 53分钟前 | python · Python ast NodeTransformer fix_missing_locations
- Python ast.NodeTransformer 修改语法树后怎么补齐位置信息
- 389浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 977次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 983次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1100次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1070次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 909次使用
-
- Go GORM 事务详细介绍
- 2023-01-07 187浏览
-
- golang gorm错误处理事务以及日志用法示例
- 2023-02-16 412浏览
-
- golang db事务的统一封装的实现
- 2023-01-07 114浏览
-
- golang实现mysql数据库事务的提交与回滚
- 2023-01-07 241浏览
-
- golang如何优雅的编写事务代码示例
- 2022-12-23 101浏览
