如何解决Django多数据库迁移时重复创建表的问题?
解决Django多数据库迁移时重复创建表的问题
问题场景
在单个Django应用中同时使用SQLite(auth_db)和两个PostgreSQL数据库(postgres_db_1、postgres_db_2),执行迁移时出现两个问题:
- 默认逻辑下,所有业务表会被创建到所有数据库(包括SQLite)
- 若在路由的
allow_migrate中直接返回False,仅会生成django_migrations表,无法正常创建对应数据库的业务表
需要实现:每个模型仅在指定的目标数据库创建表,其他数据库不生成任何该模型的相关表及迁移记录。
解决方案
核心是优化数据库路由的allow_migrate方法,精准控制每个模型与数据库的匹配关系,对不匹配的组合明确返回False,而非None(None会让Django继续询问后续路由,最终触发默认迁移逻辑)。
修改后的router.py代码
class sqlite_Router: route_app_labels = {'auth', 'contenttypes', 'admin', 'sessions'} database = 'auth_db' def allow_migrate(self, db, app_label, model_name=None, **hints): # 仅处理自身负责的内置应用,只允许在auth_db迁移 if app_label in self.route_app_labels: return db == self.database # 对于test应用的模型,直接阻止在SQLite迁移 if app_label == 'test': return False # 其他情况返回None,交给后续路由处理 return None class postgres_router_1: route_app_labels = {'test'} database = 'postgres_db_1' def allow_migrate(self, db, app_label, model_name=None, **hints): model = hints.get('model') if app_label not in self.route_app_labels: return False # 仅允许指定database为postgres_db_1的模型在当前数据库迁移 if model: usedb = getattr(model._meta, 'database', None) if usedb == self.database: return db == self.database # 不匹配的模型直接阻止迁移 return False class postgres_router_2: route_app_labels = {'test'} database = 'postgres_db_2' def allow_migrate(self, db, app_label, model_name=None, **hints): model = hints.get('model') if app_label not in self.route_app_labels: return False # 仅允许指定database为postgres_db_2的模型在当前数据库迁移 if model: usedb = getattr(model._meta, 'database', None) if usedb == self.database: return db == self.database # 不匹配的模型直接阻止迁移 return False
关键逻辑说明
SQLite路由(sqlite_Router):
- 仅允许Django内置应用(auth、admin等)在
auth_db迁移 - 明确阻止
test应用的任何模型在SQLite数据库创建
- 仅允许Django内置应用(auth、admin等)在
PostgreSQL路由(postgres_router_1/2):
- 仅处理
test应用的模型,其他应用直接返回False - 只有当模型的
database元属性与当前路由负责的数据库匹配,且目标数据库一致时,才允许迁移 - 所有不匹配的情况直接返回
False,避免后续路由触发默认迁移
- 仅处理
正确执行迁移
迁移时需指定目标数据库,避免不必要的操作:
# 迁移内置应用到SQLite python manage.py migrate --database=auth_db # 迁移test应用模型到postgres_db_1 python manage.py migrate test --database=postgres_db_1 # 迁移test应用模型到postgres_db_2 python manage.py migrate test --database=postgres_db_2
额外优化建议
不要直接修改models.options.DEFAULT_NAMES,可以通过自定义元类更规范地添加database元属性:
from django.db import models class DatabaseMeta(models.base.ModelBase): def __new__(cls, name, bases, attrs): new_class = super().__new__(cls, name, bases, attrs) if 'database' in attrs.get('Meta', {}).__dict__: setattr(new_class._meta, 'database', attrs['Meta'].database) return new_class # 使用自定义元类 class postgres_one_test(models.Model, metaclass=DatabaseMeta): postgres_test_one = models.CharField(unique=True, max_length=30) class Meta: app_label = 'test' database = 'postgres_db_1'
内容的提问来源于stack exchange,提问作者Hadi
相关产品推荐
相关产品推荐

