Flask-SQLAlchemy启用binds时必须显式配置SQLALCHEMY_DATABASE_URI(即使为空),否则抛KeyError: 'default';SQLALCHEMY_BINDS中key不可为'default',模型用__bind_key__指定非默认库,db.create_all()默认只建default库表,跨bind事务不被SQLite支持。

Flask-SQLAlchemy 的 binds 怎么配才不报 KeyError: 'default'
根本原因是:只要启用了 binds,'default' 就不再是可选的——它必须显式出现在配置里,哪怕只指向一个数据库。很多人删了默认配置、只留 SQLALCHEMY_BINDS,结果一初始化就崩。
-
SQLALCHEMY_DATABASE_URI必须存在(哪怕设为空字符串或占位值),否则 Flask-SQLAlchemy 内部会尝试读取它并抛KeyError: 'default' -
SQLALCHEMY_BINDS是个 dict,key 是 bind 名(如'slave'),value 是对应数据库 URL;key 不能叫'default',那是保留名 - 模型定义时用
__bind_key__ = 'slave'指向非默认库,不设则走default - 如果想让所有模型默认走 slave,仍需把 slave URL 塞进
SQLALCHEMY_DATABASE_URI,再把 master 单独放进SQLALCHEMY_BINDS,而不是反过来
读写分离时,session.execute() 和 db.session.execute() 走的是哪个库
它们都走 default 绑定,除非显式指定 bind。原生 SQL 执行不自动识别模型上的 __bind_key__,这点极易误判。
- 写法
db.session.execute("SELECT ...", bind='slave')才会发到 slave 库;漏掉bind参数就一定走 default -
session.execute()是db.session实例方法,和db.session.execute()等价,不存在“更底层就绕过 binds”的说法 - 用
text()包裹 SQL 时,bind 仍需手动传,例如:db.session.execute(text("SELECT 1"), bind='slave') - 事务内跨 bind 执行会报错:
sqlite does not support multiple databases in a single transaction(SQLite 尤其明显)
为什么 db.create_all() 只建了 default 库的表
因为 create_all() 默认只操作 default 绑定。多库场景下,它不会遍历 SQLALCHEMY_BINDS 自动建表,这是设计使然,不是 bug。
- 建 slave 表要手动调用:
db.create_all(bind='slave')或db.create_all(binds=['slave', 'master']) - 如果某个模型设了
__bind_key__ = 'slave',但没对slave调用create_all,查的时候会直接报no such table - 测试环境常用做法:在
app.config['SQLALCHEMY_BINDS']里加个'test_slave',然后单独建表,避免污染主库 - 注意迁移工具(如 Flask-Migrate)默认也只管 default,多库需配合
--multidb和自定义 env.py
动态切换 bind(比如按请求头选读库)怎么避免 session 混乱
不能靠改 db.session.bind,那会污染全局 session 实例。正确方式是每次查询前用新 session 或显式 bind 参数。
立即学习“Python免费学习笔记(深入)”;
- 别碰
db.session.bind = xxx,Flask-SQLAlchemy 的 session 是 scoped,改了会影响后续所有请求 - 推荐做法:用
db.session.using_bind('slave').execute(...)(v3.0+),或老版本用db.get_engine(app, bind='slave')配合sessionmaker临时建 session - 如果用
query接口,得写成User.query.bind('slave').all(),但注意这仅对 query 对象生效,不改变 session 状态 - 最容易被忽略的一点:gunicorn 多 worker 下,engine 和 connection pool 是进程级的,bind 切换本身无锁问题,但连接数上限要按总 bind 数 × 并发数来算










