You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

关键逻辑说明

  1. SQLite路由(sqlite_Router):

    • 仅允许Django内置应用(auth、admin等)在auth_db迁移
    • 明确阻止test应用的任何模型在SQLite数据库创建
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:07:51