还原 Django 1.7 RemoveField 迁移
Revert Django 1.7 RemoveField migration
如果我有一个不可为 null 的模型字段,将其删除并创建一个迁移,该迁移将变得不可逆:
考虑以下模型:
class Foo(models.Model):
bar = models.TextField()
test = models.TextField() # This field is to go away, bye-bye!
和迁移:
# app/migrations/003_remove_foo_test.py
class Migration(migrations.Migration):
dependencies = [
('app', '0002_foo_test'),
]
operations = [
migrations.RemoveField(
model_name='foo',
name='test',
),
]
取消应用此迁移会引发异常:
$ src/manage.py migrate app 0002
Operations to perform:
Target specific migration: 0002_foo_test, from app
Running migrations:
Unapplying app.0003_remove_foo_test...Traceback (most recent call last):
...
django.db.utils.IntegrityError: column "test" contains null values
当然,这是预期的行为,它是 clearly documented,我不是在问为什么会这样:
Bear in mind that when reversed this is actually adding a field to a
model; if the field is not nullable this may make this operation
irreversible (apart from any data loss, which of course is
irreversible).
然而,我们都会犯错误,有时我们只是需要以以某种方式逆转字段删除,即使这意味着手动提供所有反向非空字段的临时存根值。例如,South migrations 可选地允许反转此类操作(通过询问开发人员是否为恢复的字段提供默认值,或不允许反向迁移),这似乎不是所有新花式 Django 1.7 迁移的情况.
问题:使用 Django 1.7+ 迁移(假设它已经发生)撤消字段删除的easiest/fastest方法是什么?它不一定需要要使用 Python 完全编写脚本,一组手动说明即可。
如果您试图使未来的迁移可逆,您可以尝试将该字段作为三个迁移移除。
- 使字段可为空
- 数据迁移。前进,不要做任何事。向后,将空值转换为存根值。
- 删除字段
这三个步骤中的每一个都应该是可逆的。
如果您已经 运行 迁移并且需要撤消它,您可以
- 手动添加字段,允许空值
- 将空值转换为存根值
- 手动添加非空约束
- 使用
--fake
迁移到之前的迁移
您可以手动编辑您的迁移,并在 RemoveField
之前的字段中添加具有默认值的 AlterField
。即使在应用迁移之后它也应该是安全的。这将使 RemoveField
之后发生的事情变得可逆。
一个例子。在模型 summary
中具有名为 profit
的字段,该字段在删除之前定义如下:
profit = models.PositiveIntegerField(verbose_name='profits')
你应该在它的 RemoveField
之前添加一个 AlterField
这样的:
migrations.AlterField(
model_name='summary',
name='profit',
field=models.PositiveIntegerField(verbose_name='profits', default=0),
preserve_default=False,
),
最简单的方法可能是使用 migrations.RunSQL
您可以编辑迁移,使您的 operations
列表如下所示:
operations = [
sql=[('alter table foo_test drop test)],
reverse_sql=[('alter table foo_test add test varchar)]
]
这将是一个棘手的解决方案,但其他任何解决方案都可能如此。
只需将 'default' 和 'preserve_default' 添加到旧迁移中的 AddField 或 AddModel 中,Django 就会知道它必须使用提供的默认值重新创建列
如果我有一个不可为 null 的模型字段,将其删除并创建一个迁移,该迁移将变得不可逆:
考虑以下模型:
class Foo(models.Model):
bar = models.TextField()
test = models.TextField() # This field is to go away, bye-bye!
和迁移:
# app/migrations/003_remove_foo_test.py
class Migration(migrations.Migration):
dependencies = [
('app', '0002_foo_test'),
]
operations = [
migrations.RemoveField(
model_name='foo',
name='test',
),
]
取消应用此迁移会引发异常:
$ src/manage.py migrate app 0002
Operations to perform:
Target specific migration: 0002_foo_test, from app
Running migrations:
Unapplying app.0003_remove_foo_test...Traceback (most recent call last):
...
django.db.utils.IntegrityError: column "test" contains null values
当然,这是预期的行为,它是 clearly documented,我不是在问为什么会这样:
Bear in mind that when reversed this is actually adding a field to a model; if the field is not nullable this may make this operation irreversible (apart from any data loss, which of course is irreversible).
然而,我们都会犯错误,有时我们只是需要以以某种方式逆转字段删除,即使这意味着手动提供所有反向非空字段的临时存根值。例如,South migrations 可选地允许反转此类操作(通过询问开发人员是否为恢复的字段提供默认值,或不允许反向迁移),这似乎不是所有新花式 Django 1.7 迁移的情况.
问题:使用 Django 1.7+ 迁移(假设它已经发生)撤消字段删除的easiest/fastest方法是什么?它不一定需要要使用 Python 完全编写脚本,一组手动说明即可。
如果您试图使未来的迁移可逆,您可以尝试将该字段作为三个迁移移除。
- 使字段可为空
- 数据迁移。前进,不要做任何事。向后,将空值转换为存根值。
- 删除字段
这三个步骤中的每一个都应该是可逆的。
如果您已经 运行 迁移并且需要撤消它,您可以
- 手动添加字段,允许空值
- 将空值转换为存根值
- 手动添加非空约束
- 使用
--fake
迁移到之前的迁移
您可以手动编辑您的迁移,并在 RemoveField
之前的字段中添加具有默认值的 AlterField
。即使在应用迁移之后它也应该是安全的。这将使 RemoveField
之后发生的事情变得可逆。
一个例子。在模型 summary
中具有名为 profit
的字段,该字段在删除之前定义如下:
profit = models.PositiveIntegerField(verbose_name='profits')
你应该在它的 RemoveField
之前添加一个 AlterField
这样的:
migrations.AlterField(
model_name='summary',
name='profit',
field=models.PositiveIntegerField(verbose_name='profits', default=0),
preserve_default=False,
),
最简单的方法可能是使用 migrations.RunSQL
您可以编辑迁移,使您的 operations
列表如下所示:
operations = [
sql=[('alter table foo_test drop test)],
reverse_sql=[('alter table foo_test add test varchar)]
]
这将是一个棘手的解决方案,但其他任何解决方案都可能如此。
只需将 'default' 和 'preserve_default' 添加到旧迁移中的 AddField 或 AddModel 中,Django 就会知道它必须使用提供的默认值重新创建列