Django多数据库路由核心是DatabaseRouter类的四个方法,以首个非None返回值为准决定数据库alias;读写分离需覆盖db_for_read和db_for_write,allow_migrate须精准控制迁移目标库。

什么是 Django 多数据库路由的核心机制
Django 的数据库路由不是自动生效的,它完全依赖 DatabaseRouter 类中四个方法的返回值:当执行 db_for_read、db_for_write、allow_relation、allow_migrate 时,Django 会逐个调用你定义的路由类对应方法,并以第一个非 None 返回值为准。没返回值(即返回 None)就走默认库;返回字符串(如 'slave')才真正切换数据库。
怎么写一个最小可用的读写分离路由类
最简实现只需覆盖 db_for_read 和 db_for_write,其他两个方法可直接返回 None 或按需判断。注意:Django 不会帮你做连接池或负载均衡,路由只决定「用哪个 alias」,实际连接由 DATABASES 配置里的 HOST/PORT 决定。
常见错误是把 db_for_read 写成只对特定 model 生效——其实它接收 model 参数,但绝大多数场景下应忽略它,统一走从库;否则容易漏掉 auth.User.objects.all() 这类跨 model 查询。
-
db_for_write必须返回主库 alias(如'default'),任何写操作(save()、delete()、bulk_create())都必须经过它 -
db_for_read可根据请求上下文区分:比如检查threading.local()中是否标记了「强制主库读」,或简单轮询['slave1', 'slave2'] - 如果用了
select_related()或prefetch_related(),关联表读取也会走db_for_read,所以从库必须和主库结构一致且延迟可控
allow_migrate 为什么不能直接 return True
迁移命令(python manage.py migrate)默认只作用于 default 库,但当你有多个数据库时,Django 会为每个库调用 allow_migrate 来决定是否执行该迁移。若无条件返回 True,会导致迁移被重复应用到所有库(包括从库),而从库通常不允许 DDL 操作,直接报错 OperationalError: cannot execute INSERT in a read-only transaction。
立即学习“Python免费学习笔记(深入)”;
正确做法是:仅对目标库放行,例如让 auth 相关 migration 只在 default 上运行:
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label == 'auth':
return db == 'default'
return None
注意 return None 表示「不干预」,Django 会继续用默认逻辑(即只在 default 执行);而 return False 是明确禁止,return True 是明确允许——二者都必须精准匹配业务需求。
读写分离后 QuerySet 的 .using() 还要不要用
要,而且得更小心。显式调用 .using('default') 会绕过路由,直接指定库;但如果你在视图里写了 User.objects.using('slave').all(),而路由本身又把读导向 slave,结果就是双重指定,虽不报错但掩盖了路由逻辑,后期维护极易混乱。
建议:业务代码里尽量避免 .using(),把所有路由逻辑收口到 DatabaseRouter;只有两类情况例外:
- 需要强一致性读(如刚创建用户立刻查 profile),用
.using('default')强制走主库 - 跨库 join(Django 原生不支持),拆成两次查询后内存合并,此时必须显式
.using()指定各自库
另外,transaction.atomic(using='default') 会锁定主库事务,但不会影响路由中的读操作——也就是说,事务内 User.objects.all() 仍可能走到从库,造成幻读。真要保证一致性,得在事务内也强制 .using('default')。



















