Django在多租户数据库上运行迁移
在一个带有一些自制(但接近可用插件方法)多租户实现的Django应用程序上,我想使用South运行一个迁移(这次是一个简单的add_列),该迁移可以应用于所有模式。我有一个非常接近的配置 如果可能的话,我想跳过任何纯SQL查询。我可以从ORM中正确地获得模式名称列表,但是我想知道是否有可能以某种适当的方式从各种模式中访问表 我有一个钩子,可以在某种程度上通过参数更改DB_主机和DB_模式,但我认为不能在South的正向迁移方法中以这种方式清晰地循环 这个问题很高层次,但我主要想知道是否有人必须面对同样的问题,我很好奇是否有聪明的方法来处理它 问候,,Django在多租户数据库上运行迁移,django,django-south,multi-tenant,database-migration,Django,Django South,Multi Tenant,Database Migration,在一个带有一些自制(但接近可用插件方法)多租户实现的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
表,并在模式中安装具有该名称的视图,该视图在查询时返回上述联合,并具有“而不是插入”触发器以写入重命名的表