2015-03-05 4 views
12

Если у меня есть ненулевое поле модели, удалить его и создать миграцию, что миграция становится необратимой:Revert Джанго 1,7 RemoveField миграция

Рассмотрим следующую модель:

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', 
     ), 
    ] 

Unapplying эту миграцию бросает исключение:

$ 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 и я не спрашиваю, почему это происходит:

Имейте в виду, что при обратном это на самом деле добавления поля к модели; если поле не является нулевым, это может привести к необратимой операции (кроме потери данных, которая, конечно же, является необратимой).

Однако, все мы делаем ошибки, и иногда мы просто нужно, чтобы каким-то образом отменить удаление поля, даже если это означает вручную предоставление специального значения заглушки для всех отмененных непустых полей. Например, южные миграции необязательно позволяют отменить такие операции (попросив разработчика указать, следует ли предоставлять по умолчанию восстановленные поля или запретить обратную миграцию), что, похоже, не относится к всем новым причудливым переходам Django 1.7 ,

Вопрос: что является самым простым и быстрым способом отменить удаление поля с помощью переноса Django 1.7+ (предположим, что это уже произошло)? Это не обязательно должно быть полностью написано с помощью Python, это сделает набор ручных инструкций.

ответ

11

Вы можете вручную эд это ваша миграция и добавьте AlterField со значением по умолчанию для поля непосредственно перед RemoveField. Это должно быть безопасно даже после применения миграции.Это сделает 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, 
    ), 
+0

'AlterField' не принимает аргумент' preserve_default' (но в этом случае он, похоже, не нужен, так как 'default' никак не попадает в схему базы данных). Помимо этого, это, по-видимому, правильное и оптимальное решение. Благодаря! –

+0

Согласно [документации] (https://docs.djangoproject.com/en/1.7/ref/migration-operations/#django.db.migrations.operations.AlterField), да, но это было добавлено в 1.7. 1. И в этом случае preserve_default не имеет значения. Он действительно попадает в базу данных, но только на короткое время. – GwynBleidD

+0

Это не работает в Django 1.8 – jess

3

Если вы хотите, чтобы будущие миграции были обратимы, вы можете попробовать удалить поле в виде трех миграций.

  1. Сделать поле обнуляемым
  2. миграции данных. Вперед, ничего не делай. В обратном порядке преобразуйте значения null в значение stub.
  3. Удалить поле

Каждый из этих трех шагов должно быть обратимым.

Если вы уже запустили миграцию и нужно повернуть его вспять, вы можете

  1. вручную добавить поле, позволяя обнуляет
  2. преобразовать аннулирует до значения тупиковой
  3. вручную добавить не нулевое ограничение
  4. Migrate с --fake к предыдущей миграции
+0

Я не совсем уверен, что «вручную добавить поле "в шаге 1. Я бы очень хотел избежать ручного запуска ALTER TABLE .. ADD COLUMN, это то, что всегда делает ORM-слой и всегда должен делать. Программисту не нужно повторять все небольшие трюки, которые ORM делает (имена полей, управление индексами и т. Д.). –

+0

Ручной запуск SQL не идеален, но иногда у вас могут не быть других параметров. У кого-то еще может быть лучшее предложение. Возможно, вы сможете заставить Django генерировать SQL с помощью. ./manage.py sqlmigrate --backwards'. – Alasdair

+0

Я могу подтвердить, что './manage.py sqlmigrate --backwards' дает правильный SQL для этой миграции, и что это не является болезненным для запуска, если вы используете IDE, например PyCharm; подход миграции к редактированию не работал для меня при обратной миграции. – Symmetric