Django多数据库迁移

30

我在创建数据迁移方面遇到了困难。我在应用程序中使用两个数据库。我在settings.py中配置了数据库,还创建了一个路由器,就像Django文档中介绍的那样。

# settings.py
DB_HOST = 'localhost'
DATABASES = {
'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'helios',
    'HOST': DB_HOST,
    'OPTIONS': {
        'read_default_file': join(dirname(__file__), 'default.cnf'),
    },
},
'other': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'gala_pol',
    'HOST': DB_HOST,
    'OPTIONS': {
        'read_default_file': join(dirname(__file__), 'other.cnf'),
    },
},

DATABASE_APPS_MAPPING = {
    'contenttypes': 'default',
    'auth': 'default',
    'admin': 'default',
    'sessions': 'default',
    'messages': 'default',
    'staticfiles': 'default',
    'woodsmen': 'default',
    'helios': 'default',
    'hush': 'default',
    'hunt': 'other',
    'meat': 'other',
    'beast': 'other',
}

# routers.py

class DatabaseAppsRouter(object):

    def db_for_read(self, model, **hints):

        if model._meta.app_label in settings.DATABASE_APPS_MAPPING:
            return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
        return None

    def db_for_write(self, model, **hints):

        if model._meta.app_label in settings.
            return settings.DATABASE_APPS_MAPPING[model._meta.app_label]
        return None

    def allow_relation(self, obj1, obj2, **hints):

        db1 = settings.DATABASE_APPS_MAPPING.get(obj1._meta.app_label)
        db2 = settings.DATABASE_APPS_MAPPING.get(obj2._meta.app_label)
        if db1 and db2:
            return db1 == db2
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):

        if db in settings.DATABASE_APPS_MAPPING.values():
            return settings.DATABASE_APPS_MAPPING.get(app_label) == db
    elif app_label in settings.DATABASE_APPS_MAPPING:
            return False

这是其中一个应用程序的模型和迁移:

# hunt.models.py

class Dish(models.Model):
    """
    Investigation case
    """
    display_name = models.CharField(max_length=64, unique=True)
    department = models.ForeignKey(Kitchen, null=True)
    case_type = models.PositiveSmallIntegerField(choices=CASE_TYPE_CHOICES, default=DEF_CASE_TYPE)
    created_at = models.DateTimeField(blank=True, null=True)
    comment = models.CharField(max_length=256, blank=True, null=True)

    class Meta:
        verbose_name = 'case'
        app_label = 'hunt'

    def __unicode__(self):
        return (u'%s (%s)' % (self.display_name, self.created_at)).strip()


# hunt.migrations.0001_initial.py

class Migration(migrations.Migration):

    app_label = 'hunt'

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Dish',
            fields=[
                ('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)),
                ('display_name', models.CharField(max_length=64, unique=True)),
                ('case_type', models.PositiveSmallIntegerField(default=0, choices=[(0, 'Unknown'), (1, 'General'), (2, 'Terror'), (3, 'Narco'), (4, 'Fraud'), (5, 'Slavery'), (6, 'Traffic'), (7, 'RICO'), (8, 'War'), (9, 'Cyber'), (20, 'Other')])),
                ('created_at', models.DateTimeField(null=True, blank=True)),
                ('comment', models.CharField(max_length=256, null=True, blank=True)),
            ],
            options={
                'verbose_name': 'case',
            },
        ),
    ]

# hunt.migrations.0002_add_hunts.py


def create_initial_hunts(apps, schema_editor):

    if settings.DEBUG:    
        print('\nContent added')


class Migration(migrations.Migration):
    dependencies = [
        ('hunt', '0001_initial'),
    ]


    operations = [
        migrations.RunPython(create_initial_hunts, hints={'schema_editor': 'other'}),
    ]

问题是:当我运行“migrate”命令时,只有连接到默认数据库的应用程序才会进行迁移。其他应用程序中的迁移永远不会运行。如果我使用--database选项为这样的应用程序启动迁移-它可以正常工作。
我该如何指定每个迁移的数据库?路由器难道不应该管理这个吗?还是我错过了其他什么?
2个回答

40

必须对每个数据库运行migrate一次,并使用--database指定目标。每次它都会查看你的路由器,以确定在该数据库上实际执行哪些迁移。

我猜设计成这样是为了更加明确而不是隐含。例如,您的工作流可能要求您在不同时间对不同数据库进行迁移。

请注意,您将无法从输出中了解到实际执行了哪些迁移,因为:

如果allow_migrate()返回False,则在对db运行migrate时,将会静默跳过model_name的任何迁移操作。


1
谢谢,我最终确实做到了这一点。 - Leon Kladnitsky
13
为什么 Django 提供了 DATABASE_ROUTERS,却不支持多个数据库的问题呢?这很奇怪。 - dannydedog
迁移为两个数据库创建相同的认证结构。默认端是默认的Django表,另一端是相同的默认Django表加上所选模板中的表。这是诀窍吗? - marcelo.delta

1

使用这些方便的帮助程序,您可以在特定数据库上运行Python/SQL迁移

[帮助程序]

from django.db.migrations import RunPython, RunSQL

def run_python_specific_db_migration(migration_func, use_db):
    """calls RunPython command only for specific database """
    return RunPython(migration_func, hints={'use_db': use_db})


def run_sql_specific_db_migration(sql_commands, use_db):
    """Runs one or list of sql commands only on specific database """
    return RunSQL(sql_commands, hints={'use_db': use_db})

# ** Your specific db_helpers for your DB_KEY **

def run_sql_your_db_migration(sql_commands):
    return run_sql_specific_db_migration(sql_commands, use_db=DB_KEY)

def run_python_your_db_migration(migration_func):
    return run_python_specific_db_migration(migration_func, use_db=DB_KEY)

[用法]

def data_migration(apps, schema_editor):
    ...your data migration logic..better to wrap with @atomic...

class Migration(migrations.Migration):
    operations = [ run_python_your_db_migration(data_migration) ]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接