【问题标题】:How to ignore a specific migration?如何忽略特定的迁移?
【发布时间】:2019-10-23 08:34:29
【问题描述】:

我有这样的迁移:

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0020_auto_20191023_2245'),
    ]

    operations = [
        migrations.AddField(
            model_name='agenda',
            name='theme',
            field=models.PositiveIntegerField(default=1),
        ),
    ]

但它会引发错误:

django.db.utils.ProgrammingError: column "theme" of relation "app_agenda" already exists

没问题,我把这个错误包装成这样:

from django.db import migrations, models, ProgrammingError


def add_field_theme_to_agenda(apps, schema_editor):
    try:
        migrations.AddField(
            model_name='agenda',
            name='theme',
            field=models.PositiveIntegerField(default=1),
        ),
    except ProgrammingError as e:  # sometimes it can exist
        if "already exists" not in str(e):
            raise


class Migration(migrations.Migration):
    dependencies = [
        ('app', '0020_auto_20191023_2245'),
    ]
    operations = [
        migrations.RunPython(add_field_theme_to_agenda),
    ]

这就像一个魅力,并且完成了以下所有迁移。

我的问题是,每次我运行“makemigrations”时,Django 都会再次添加迁移(= 我的问题顶部的那个)。我猜是因为它在迁移中看不到它,因为我的代码混淆了它。

如何使用迁移来规避这个问题(不要说“这个问题出在您的数据库上,请更正您的数据库”之类的答案)?

【问题讨论】:

  • 也许你可以运行 migrate --fake your_migration

标签: django django-migrations


【解决方案1】:

Django 正在重新创建迁移,因为当您在 RunPython 操作中手动执行操作时,它无法理解添加的字段。您可以尝试的是(我自己没有尝试过),将 AddField 操作子类化以创建自定义 AddField 操作,您可以在其中处理异常。像下面这样的东西可以工作:

from django.db import migrations, models, ProgrammingError


class AddFieldIfNotExists(migrations.AddField):

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        try:
            super().database_forwards(app_label, schema_editor, from_state,
                                      to_state)
        except ProgrammingError as e:  # sometimes it can exist
            if "already exists" not in str(e):
                raise


class Migration(migrations.Migration):
    atomic = False
    dependencies = [
        ('app', '0070_auto_20191023_1203'),
    ]

    operations = [
        AddFieldIfNotExists(
            model_name='agenda',
            name='theme',
            field=models.PositiveIntegerField(default=1),
        ),
    ]

【讨论】:

  • 这是一个很好的建议,但它不适用于错误 django.db.utils.InternalError: current transaction is aborted, commands ignored until end of transaction block。我会尝试让它工作然后编辑你的答案+如果你不介意检查它是否有效(如果我找到解决方案)。
  • 嗯,也许你可以通过调整处理异常的方式来克服这个问题,也许在更高级别上捕获异常可能会起作用(迁移引擎可能需要在其他地方捕获 ProgrammingError)。
  • @OlivierPons 当然,我也很高兴在这条道路上看到完整的解决方案。
  • 即使我做一个“全局”try/except Exception 来捕捉它们,我仍然会得到django.db.utils.InternalError: current transaction is aborted, commands ignored until end of transaction block
  • 完成! \o/ 非常感谢。问题是因为它在每次操作后检查(这里只有一个!)一切正常。解决方案是添加atomic = False(请参阅我对您自己答案的修改)。再次感谢!
【解决方案2】:

您可以在迁移命令中利用 --fake 标志

./manage.py migrate app_name migration_number --fake

这会将迁移标记为完成。

【讨论】:

  • 即使完成了它也没有真正解决......我的意思是我的系统可以允许进行一大堆迁移,即使我的(中间)是一个问题(= 不创建 if它存在)。主要目标是始终在一行中运行所有内容 =“migrateeverything”,而无需专门为此迁移执行--fake。 (这是给码头工人的……)
  • 如果您想迁移以使用“标准”迁移命令运行,请将其转换为内部没有操作的自定义命令,仅使用 pass 语句设置您的 add_field_theme_to_agenda 函数,并且将始终运行正常
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-29
  • 2013-12-13
  • 2017-10-17
  • 1970-01-01
  • 2019-11-06
  • 1970-01-01
相关资源
最近更新 更多