DatabaseRouter类必须实现db_for_read、db_for_write、allow_relation、allow_migrate四个方法;缺一即报AttributeError;其中allow_relation和allow_migrate最易遗漏,导致跨库关联失败或迁移异常。

怎么写一个能用的 DatabaseRouter 类
必须实现四个方法,少一个 Django 就会报 AttributeError: 'YourRouter' object has no attribute 'db_for_read'。实际项目里最容易漏的是 allow_relation 和 allow_migrate,不写它们,跨库 ForeignKey 或迁移命令(python manage.py migrate)直接失败。
典型结构如下:
class PrimaryReplicaRouter:
def db_for_read(self, model, **hints):
return 'replica'
<pre class='brush:python;toolbar:false;'>def db_for_write(self, model, **hints):
return 'primary'
def allow_relation(self, obj1, obj2, **hints):
# 同库或都走 primary/replica 才允许关联
if obj1._state.db in ('primary', 'replica') and \
obj2._state.db in ('primary', 'replica'):
return True
return False
def allow_migrate(self, db, app_label, model_name=None, **hints):
# 写库只跑迁移,读库不跑
if db == 'replica':
return False
return True注意: allow_migrate 的返回值是布尔值,不是数据库名;返回 False 表示“这个迁移不该在该 db 上执行”,不是“跳过迁移”。
为什么 settings.DATABASES 配置完还连不上 replica
光在 DATABASES 里加了 'replica' 配置项没用——Django 不会自动把查询分发过去。必须在 settings.DATABASE_ROUTERS 中显式注册路由类,否则所有操作默认走 'default'。
立即学习“Python免费学习笔记(深入)”;
常见错误配置:
强戈环形山: q15040409-刻画家功能概述强戈环形山: q15040409-刻画家是一项面向实际任务的技能,主要用于平顶山: 维西翁: 1:1.0. 塔格斯: rap.github: 自动化。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。核心要点使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。从功能定位来看,该
- 忘了加引号:
DATABASE_ROUTERS = ['myapp.routers.PrimaryReplicaRouter'](字符串路径,不是类实例) - 路径写错:比如模块不存在、类名拼错,Django 启动时不会报错,但路由完全不生效
- 多个 router 顺序冲突:后注册的 router 可能覆盖前者的
db_for_read判断,建议只配一个 router 类,内部用逻辑分支处理
using() 和 router 冲突时谁赢
using() 优先级更高。比如 User.objects.using('replica').all() 会强制走 replica,绕过 router 的 db_for_read 判断。这在调试或特殊场景有用,但也容易掩盖路由配置问题。
容易踩的坑:
- 写了
using('replica')却没在DATABASES里配'replica'→ 报错ConnectionDoesNotExist: The connection 'replica' doesn't exist - 在
allow_migrate里返回False,但又对replica手动执行migrate --database=replica→ 迁移被拒绝,但错误信息不明确,只显示 “No migrations to apply” - 使用
select_related()或prefetch_related()跨模型时,如果关联表在不同库,Django 无法生成合法 SQL,直接抛DatabaseError
读写分离在事务里失效怎么办
只要进了 transaction.atomic(),Django 默认把所有读也切到写库(primary),这是硬编码行为,router 无法干预。这是为了保证事务内读写一致性,但代价是 replica 完全闲置。
如果你确定某段事务内的读可以容忍旧数据,只能手动指定:
User.objects.using('replica').filter(...)但要注意:using() 在事务中仍有效,只是不能和 select_related 混用——后者会忽略 using 并尝试 join,而跨库 join 不被支持。
真正难处理的是带 ForeignKey 的复杂查询。一旦模型 A 和 B 分属不同库,Django 就没法安全地做关联查询,这时候得退回到原生 SQL 或应用层拆解逻辑。

















