在 Django 中使迁移有条件

Make Migration Conditional in Django

具体用例为:

A Django module wants to create an extension during migration. If the DB user that is used to run the migration is not superuser, this fails.

有几种方法可以解决这个问题,一种方法是(假设)检查迁移文件是否安装了扩展,如果没有安装,则只 运行 SQL 代码。

然而,经过一些研究,似乎 Django 的 RunSQL 不能 return 结果,并且随后根据先前操作的结果排除操作是不可能的。还有其他方法可以实现这一目标吗? (例如子类化 RunSQL?)

任何基于 Django 迁移、Django 设置或 Postgres 内部的解决方案(一个 SQL 语句,只有在特定条件为真时才能达到 运行 CREATE EXTENSION)。

(请注意,我提到 django-pgcrypto-fields 只是为了说明。我很想知道这样的解决方案是否普遍存在。)

编辑以回答 Anentropic 的评论: 该解决方案必须在 运行 宁 testjenkins 命令时起作用。这意味着,手动调用 --fake-initial 或类似方法来避免 运行 迁移不是一种选择。如果您能解释如何 test 伪造某些迁移,非常欢迎。

我目前的解决方案是将以下内容添加到 settings:

MIGRATION_MODULES = {
    'pgcrypto_fields': 'do-not-run-migrations'
}

但这将禁用所有有问题的迁移,而不仅仅是有问题的迁移。在这种情况下,它可能会起作用,但我认为这是一种幸运但丑陋的解决方法。

实际上,Django 的 HStoreExtension 仅在必要时执行 运行。但当然,它仍然需要超级用户访问权限。对于 pgcrypto,可以创建一个类似于 HStoreExtension 的 class。

在测试中实现 运行 的设置仍然需要超级用户访问权限,因此设置必须如下所示:

INSTALLED_APPS = (
    ...
    'django.contrib.postgres',
    ...
)

if sys.argv[1] in ['test', 'jenkins']:
    # Install extension hstore (requires superuser)
    INSTALLED_APPS = ('test_init',) + INSTALLED_APPS

    # a superuser
    DATABASES["default"]["USER"] = 'xxx'
    DATABASES["default"]["PASSWORD"] = 'xxx'

并且 'test_init' django 应用程序仅包含必需的 __init.py__ 和一个具有以下文件的迁移模块 0001_initial.py:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib.postgres.operations import HStoreExtension

from django.db import migrations


class Migration(migrations.Migration):

    run_before = [
        ('some_app_that_requires_hstore', '0001_initial'),
    ]

    operations = [
        HStoreExtension(),
    ]

实际上不需要检查测试模式(通过 sys.argv)来进行迁移。如上所述,HStoreExtension 对错误保持沉默。因此,即使您 运行 migrate 在没有超级用户的情况下进行生产,它也不会失败,无论是否安装了 hstore。查看它的源代码。

另见 Django's documentation of HStoreField