当前位置: 首页 > news >正文

Django多数据库实战:Mysql从逻辑隔离到跨库外键问题的解决方案

Django目录文件夹:

一、 背景与目标:为什么需要多数据库?

随着项目规模的扩大,我们经常会遇到这样的需求:将一个大型单体应用(Monolith)按照业务领域进行逻辑拆分。例如,一个“元数据平台”可能包含“元数据中心”、“主数据中心”、“数据质量中心”等多个相对独立的业务模块。

为了实现清晰的职责分离数据隔离,我们设定了以下架构目标:

  • 单一代码库,逻辑隔离:所有功能仍在同一个Django项目中开发,但每个“中心”由一组独立的App构成;
  • 数据存储隔离:每个“中心”的数据应存储在各自独立的数据库(在MySQL中)或Schema(在PostgreSQL中)中,便于管理、备份和权限控制;
  • 保持ORM便利性:尽管数据被物理隔离,我们仍希望能在Django应用层保持一定程度的关联查询能力,而不是完全退化到原生SQL。

二、 核心挑战:跨数据库的外键约束

在尝试实现这一目标时,我们遇到了一个由数据库本身限制所导致的致命问题:

MySQL(以及大多数关系型数据库)不允许跨物理数据库(Schema)创建外键(FOREIGN KEY)约束。

本身的数据库是meta_puredrf,当我们尝试在 新建的metadata_center 数据库中创建一个引用了 meta_puredrf数据库中 user_user 表的模型时,migrate 命令会失败,并抛出类似以下的错误:

django.db.utils.OperationalError: (1824, "Failed to open the referenced table 'user_user'")

这意味着,我们无法在数据库层面强制维持跨库引用的完整性。

三、 解决方案:数据库路由器 + 逻辑外键

为了解决这个问题,我们采用了一种Django官方推荐的、优雅的组合方案:数据库路由器负责“指路”,逻辑外键负责“解耦”

  • 首先,我们需要让Django知道存在多个数据库。

# settings.pyDATABASES = {'default': { # Django自带应用和用户中心使用的主数据库'ENGINE': 'django.db.backends.mysql','NAME': 'puredrftest',# ...},'metadata_center': { # 为元数据中心创建的专门数据库连接'ENGINE': 'django.db.backends.mysql','NAME': 'metadata_center',# ...},'master_data_center': { # 为未来的主数据中心预留'ENGINE': 'django.db.backends.mysql','NAME': 'master_data_center',# ...}
}

路由器是整个方案的“交通警察”,它告诉Django的ORM,每个模型的读、写、关联和迁移操作应该被路由到哪个数据库。

  • 我们在一个核心应用(如 apps/base_server)下创建一个 db_routers.py 文件。

# 文件路径: apps/base_server/db_routers.pyclass AppBasedRouter:"""一个根据 app_label 将模型路由到不同数据库的路由器。"""# 定义哪些 app 应该被路由到特定的数据库route_app_labels = {'project_management': 'metadata_center','metadata_definations': 'metadata_center','instance_management': 'metadata_center','supplier_management': 'master_data_center', # 示例}def db_for_read(self, model, **hints):"""路由读操作"""return self.route_app_labels.get(model._meta.app_label, 'default')def db_for_write(self, model, **hints):"""路由写操作"""return self.route_app_labels.get(model._meta.app_label, 'default')def allow_relation(self, obj1, obj2, **hints):"""决定是否允许两个对象之间存在关系。为了支持跨库的逻辑外键,我们在这里返回 True。"""return Truedef allow_migrate(self, db, app_label, model_name=None, **hints):"""决定一个 app 的迁移是否应该在某个数据库上运行。"""# 获取该 app 应该在的数据库target_db = self.route_app_labels.get(app_label, 'default')# 只有当目标数据库与当前正在迁移的数据库匹配时,才允许return db == target_db
  • 然后,在 settings.py 中启用这个路由器:

# settings.py
DATABASE_ROUTERS = ['apps.base_server.db_routers.AppBasedRouter']

这是解决跨库外键约束问题的关键。我们需要找到所有跨数据库的 ForeignKey,并告诉Django不要在数据库层面创建物理约束。

示例: 我们的 BaseModel1 引用了 User 模型,而 User 模型在 default 数据库,BaseModel1 的子类(如 MetaType)在 metadata_center 数据库。

# 文件路径: utils/models.pyclass BaseModel1(models.Model):# ...creator = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.SET_NULL,null=True,verbose_name="创建人",db_constraint=False # <-- 关键!)updater = models.ForeignKey(settings.AUTH_USER_MODEL, ..., db_constraint=False)deleter = models.ForeignKey(settings.AUTH_USER_MODEL, ..., db_constraint=False)# ...

db_constraint=False 的作用:

  • 保留逻辑关联: 在您的Django应用内部,instance.creator 仍然是一个有效的外键。您可以执行 instance.creator.username,Django会自动执行一次跨数据库的查询来获取用户信息。

  • 移除物理约束: 在生成数据库迁移时,Django不会为这个字段创建 FOREIGN KEY 约束,从而完美地绕过了MySQL的限制。

由于这是一个重大的架构变更,必须进行一次干净的迁移:

  1. 备份所有数据库;

  2. 删除所有业务应用 migrations 文件夹下的旧迁移文件;

  3. 清空主数据库中的 django_migrations 表;

  4. 清空所有业务数据库(如 metadata_center)中的所有表;

  5. 运行 python manage.py makemigrations 重新生成所有应用的初始迁移;

  6. 运行 python manage.py migrate,Django现在会根据路由器的指示,在正确的数据库中创建所有表;

  7. (可选)编写数据迁移脚本,将备份的数据导回到新的、按Schema划分的表中。

四、 结论

通过多数据库连接数据库路由器和**db_constraint=False** 这三大法宝的组合,我们成功地构建了一个既能实现数据物理隔离,又能保持应用层逻辑关联的健壮架构。

http://www.dtcms.com/a/389720.html

相关文章:

  • SQL Server索引优化:从原理到实战的完整指南
  • 前端-Vue自定义指令
  • 深度学习调参核心:PyTorch学习率调整策略全解析(一)(附系列PPT关键要点)
  • 如何在保证质量的前提下,快速完成一份 PPT?
  • AssemblyScript 入门教程(3)AssemblyScript 项目搭建与实战入门
  • React 实战进阶视频教程
  • 运维安全08 - 日志检测和 tcpdump (抓包) 的介绍以及使用
  • 感烟火灾探测器工程量计算
  • 数学真题分类刷题(前两章)
  • 基于文本与声学特征的渐冻症言语障碍严重程度分类研究
  • 基于语音合成的数据增强在独立说话人构音障碍严重程度分类中的应用
  • vscode 设置
  • vscode关闭coplit功能
  • ICML 2025|GAPrompt:用于3D视觉模型的几何感知点云提示
  • OCCI使用
  • 如何在命令列将.brd转成.siw
  • 贪心算法应用:欧拉路径(Fleury算法)详解
  • 第13章 时间处理
  • Python 抓包工具有哪些,抓包失败怎么办?(Python 抓包工具清单 + 常见失败原因与逐步排查)
  • 数据库模式演进的利器:Alembic 深度解析
  • 用 JMeter 打通“异步入队 + 网关限流”的高并发压测实践
  • Linux服务器从零开始-服务器安装配置
  • Python第三节基本数据类型详解与使用指南
  • 【indextts2】之四:ubuntu24.04 经常跑满12G 显存、音色与录制关系很大
  • 【学习笔记】稳定币的技术原理与信任逻辑
  • 为 Elasticsearch Ruby Client 引入 ES|QL 查询生成器
  • 平安养老险陕西分公司积极开展“保障金融权益,助力美好生活”金融教育宣传周活动
  • 多线程的特点与同步、互斥锁
  • 将npm run dev 冷启动从 90s 优化到 8.5s的实践
  • 【附源码】基于SSM的小型银行贷款系统设计与实现