数据库路由是django决定“某个查询发给哪个数据库”的决策层,而非databases配置的补充;databases仅声明可用库,路由才真正控制读写分流——db_for_read定向select到从库,db_for_write强制insert/update/delete走主库,缺任一方法即报attributeerror。

什么是数据库路由,它和DATABASES配置有什么区别?
数据库路由不是 DATABASES 设置的补充,而是 Django 决定“某个查询该发给哪个数据库”的决策层。Django 默认只用 default 数据库,哪怕你配置了多个库,不写路由,所有读写都走 default。路由是让 MyModel.objects.all() 这样的调用能自动落到 slave1,而 MyModel.objects.create() 落到 master 的关键。
它由一个或多个类实现,必须定义至少四个方法:db_for_read、db_for_write、allow_relation、allow_migrate。缺任何一个,Django 启动时就会报 AttributeError。
-
db_for_read决定 SELECT 查询去哪个库(比如返回'slave1') -
db_for_write决定 INSERT/UPDATE/DELETE 去哪个库(通常返回'default'或'master') -
allow_relation控制跨库 ForeignKey 是否允许(比如主从之间允许关联,但两个 slave 之间不允许) -
allow_migrate控制迁移命令(python manage.py migrate)往哪个库写表结构(一般只写master)
如何写一个最小可用的读写分离路由类?
最简路由不需要复杂逻辑,只要区分读写即可。假设你在 settings.py 中已配置了两个库:'default'(主库)和 'slave1'(从库),那么路由类可以这样写:
class PrimaryReplicaRouter:
def db_for_read(self, model, **hints):
return 'slave1'
def db_for_write(self, model, **hints):
return 'default'
def allow_relation(self, obj1, obj2, **hints):
db_list = ('default', 'slave1')
if obj1._state.db in db_list and obj2._state.db in db_list:
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if db == 'default':
return True
return False
注意:allow_migrate 返回 False 表示“这个迁移不要在这个库执行”,所以对 slave1 返回 False 是合理的;但如果你有多个 master(比如分片场景),就得按需判断 app_label。
别忘了在 settings.py 中注册它:
DATABASE_ROUTERS = ['myapp.routers.PrimaryReplicaRouter']
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
为什么 select_related 或 prefetch_related 会绕过路由?
因为它们本质是 JOIN 或额外的 SELECT 查询,Django 会把主查询和关联查询拆成多个独立查询,而每个查询单独走一次路由。如果主模型路由到 slave1,但关联模型没显式指定库,就可能落到 default,导致 DatabaseError: Relation does not exist —— 尤其当从库没同步完外键表结构时。
- 解决办法:强制指定数据库,例如
MyModel.objects.using('slave1').select_related('related_field') - 更稳妥的方式:在路由的
db_for_read中加入模型白名单判断,确保关联模型也走同一库 - 避免在从库上做
prefetch_related涉及跨库外键的操作,Django 不支持跨库 JOIN
事务中读操作为何仍可能落到从库?
这是最容易被忽略的陷阱。Django 在事务内(with transaction.atomic():)的所有数据库操作,包括读,都会强制使用写库(即 db_for_write 返回的库)。这是为了保证事务一致性 —— 你刚写入一条记录,紧接着 .get() 却从延迟的从库读不到,会出问题。
所以,如果你在事务里写了数据又立刻读,别指望路由能把读发到 slave1;它一定会走 default。这不是 bug,是设计使然。
若真需要事务内读从库(极少见),只能手动切换:MyModel.objects.using('slave1').filter(...),但要自己承担数据不一致风险。
真正需要关注的是:连接池是否复用、从库同步延迟是否可控、以及 allow_migrate 是否漏配导致迁移失败 —— 这些比路由逻辑本身更容易卡住上线。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










