If I have a non-nullable model field, remove it, and create a migration, that migration becomes non-reversible:
Consider the following model:
class Foo(models.Model):
bar = models.TextField()
test = models.TextField() # This field is to go away, bye-bye!
And migration:
# app/migrations/003_remove_foo_test.py
class Migration(migrations.Migration):
dependencies = [
('app', '0002_foo_test'),
]
operations = [
migrations.RemoveField(
model_name='foo',
name='test',
),
]
Unapplying this migration throws an exception:
$ 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
Of course, this is the expected behaviour, it is clearly documented and I'm not asking why this happens:
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).
However, we all make mistakes and sometimes we just need to somehow reverse a field deletion, even if that means manually providing an ad hoc stub value for all reversed non-null fields. For instance, South migrations optionally allow the reversal of such operations (by asking the developer on whether to provide a default for restored fields, or disallow reverse migration), which doesn't seem to be the case with the all new fancy Django 1.7 migrations.
Question: what is the easiest/fastest way to undo a field removal with Django 1.7+ migrations (assuming it has already happened)? It doesn't necessarily need to be fully scripted with Python, a set of manual instructions will do.
You can manually edit your migration and add AlterField
with default value for a field just before RemoveField
. It should be safe even after applying migration. That will make RemoveField
that will happen after to be reversible.
An example. Having field in model summary
named profit
that was defined before removal like that:
profit = models.PositiveIntegerField(verbose_name='profits')
you should add before RemoveField
of it an AlterField
like that:
migrations.AlterField(
model_name='summary',
name='profit',
field=models.PositiveIntegerField(verbose_name='profits', default=0),
preserve_default=False,
),