Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django在多租户数据库上运行迁移_Django_Django South_Multi Tenant_Database Migration - Fatal编程技术网

Django在多租户数据库上运行迁移

Django在多租户数据库上运行迁移,django,django-south,multi-tenant,database-migration,Django,Django South,Multi Tenant,Database Migration,在一个带有一些自制(但接近可用插件方法)多租户实现的Django应用程序上,我想使用South运行一个迁移(这次是一个简单的add_列),该迁移可以应用于所有模式。我有一个非常接近的配置 如果可能的话,我想跳过任何纯SQL查询。我可以从ORM中正确地获得模式名称列表,但是我想知道是否有可能以某种适当的方式从各种模式中访问表 我有一个钩子,可以在某种程度上通过参数更改DB_主机和DB_模式,但我认为不能在South的正向迁移方法中以这种方式清晰地循环 这个问题很高层次,但我主要想知道是否有人必须面

在一个带有一些自制(但接近可用插件方法)多租户实现的Django应用程序上,我想使用South运行一个迁移(这次是一个简单的add_列),该迁移可以应用于所有模式。我有一个非常接近的配置

如果可能的话,我想跳过任何纯SQL查询。我可以从ORM中正确地获得模式名称列表,但是我想知道是否有可能以某种适当的方式从各种模式中访问表

我有一个钩子,可以在某种程度上通过参数更改DB_主机和DB_模式,但我认为不能在South的正向迁移方法中以这种方式清晰地循环

这个问题很高层次,但我主要想知道是否有人必须面对同样的问题,我很好奇是否有聪明的方法来处理它

问候,,
马特

很好,似乎没有那么多人有经验或关心这个相当具体的问题。我在这里和那里尝试了一些东西,我也从南方邮件列表中得到了一些支持,帮助我理解了一些要点

基本上,我实施的解决方案如下:

我有一个通过South的schemamigration自动生成的非常普通的迁移文件。但是我已经将添加列删除列的表名更改为
模式。表名
。模式是通过导入多租户中间件提供的

然后,仅当架构未针对公共架构运行时,才会应用迁移。它实际上并不意味着要独立运行,或者只与数据库和模式kwargs一起运行,而是从一个迁移运行程序(一个新的django命令)运行

不幸的是,为了再次通过中间件,运行程序不得不在外部调用迁移。另一个技巧是,我们必须获得迁移的前一个状态,以便在每次租户迁移后伪造它返回到南方的前一个状态

以下是我的片段:

from subprocess import call
import os
from django.core.management.base import BaseCommand    
from south.models import MigrationHistory
from myapp.models import MyModel

class Command(BaseCommand):

    def handle(self, *args, **options):
        #the only allowed arg is the prefix version and it should have a length of 4 (i.e. 0002)
        applied = MigrationHistory.objects.filter(app_name='myapp').latest('applied')
        current_version = applied.migration[:4]
        call_args = ['python', os.path.join('bin', 'manage.py'), 'migrate', 'myorderbird.app.backups']
        if len(args) == 1 and len(args[0]) == 4:
            call_args.append(args[0])

        obje_call_args = None
        for obje in MyModel.objects.all():
            if obje.schema_exists:
                # fake the migration of the previous venue back to the current version
                if obje_call_args:
                    obje_call_args = obje_call_args[:4] + [current_version, '--fake'] + obje_call_args[len(obje_call_args)-3:]
                    call(obje_call_args)
                # migrate the venue in the loop
                obje_call_args = list(call_args)
                obje_call_args.extend(['--database={}'.format(obje.db), '--schema={}'.format(obje.schema)])
                call(venue_call_args)

这是一个解决方案的概要,发布在南方邮件列表上。这个问题的措辞与列表上发布的有点不同:这里还提到,在一个单独的模式中,有“公共”表,在所有租户之间共享。RMAT自己的答案将其称为公共模式

我的解决方案的基本思想是:保存模式中每个数据库(模式)的迁移历史。为此,我们需要使用一些数据库和Django技巧

这意味着公共架构上的应用程序迁移的历史记录保存在公共架构中,而租户应用程序迁移的历史记录保存在租户架构中——这实际上是对迁移历史表的分片。Django并不真正支持这种切分;设置按实例编写的内容很容易,但无法设置读取

因此,我建议为每个租户创建一个“租户助手”模式,其中包含一个名为
south_migrationhistory
的视图,它是来自公共模式和租户模式的
south_migrationhistory
表的联合。然后,为South MigrationHistory模型设置一个数据库路由器,指示它:

  • 同步数据库到公共和租户模式
  • 始终从租户帮助程序架构读取
  • 根据迁移所属的应用程序,写入公共或租户架构
结果允许正确处理从租户应用程序迁移到公共应用程序迁移的依赖关系;这意味着,要向前迁移,只需在
migrate--all
(或
syncdb--migrate
)命令上循环即可,无需假装向后迁移。公共架构的迁移将与循环中第一个租户的迁移一起运行,所有其他租户将“看到”它们

事后想一想,在没有助手模式的情况下也可能做到这一点,方法是重命名租户模式中的
south_migrationhistory
表,并在模式中安装具有该名称的视图,该视图在查询时返回上述联合,并具有“而不是插入”触发器以写入重命名的表