在 Django 中使迁移有条件

Posted

技术标签:

【中文标题】在 Django 中使迁移有条件【英文标题】:Make Migration Conditional in Django 【发布时间】:2015-10-28 03:21:53 【问题描述】:

具体用例是:

A Django module 想在迁移期间创建扩展。如果 用于运行迁移的数据库用户不是超级用户,这 失败。

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

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

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

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

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

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

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

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

【问题讨论】:

模块需要扩展才能工作,所以你需要找到一种方法来创建扩展。我建议以管理员用户身份创建扩展程序,然后 fake 有问题的迁移 @Anentropic 感谢您的建议。不幸的是,这不适用于manage.py test,恕我直言。我已经编辑了我的问题以回答您的评论。 如果是用于运行测试,我建议使用不同的数据库设置进行测试,您的 django db 用户可以在测试数据库上获得管理员访问权限 我知道还有其他方法可以解决具体的用例。但是我真正想知道是否有一种方法可以根据某些条件运行迁移操作(无论是通过迁移文件中的代码,还是通过 SQL 代码本身,或者通过 Django 设置)。我刚刚添加了用例作为示例。 如果你编写了迁移,你可以有条件地做一些事情,我想这对于 3rd 方迁移将是困难/不可能的 【参考方案1】:

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

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

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。

【讨论】:

以上是关于在 Django 中使迁移有条件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Ruby on Rails 迁移中使列唯一并为其编制索引?

Redis键迁移

南迁移和 django 迁移有啥区别?

django 迁移 - 从磁盘上的位置获取迁移

如何在压缩它们后删除 django 迁移?

在 Django 1.7 迁移之前运行 South 迁移的推荐方法是啥?