Мастерство миграций

В этой статье мы углубимся в мир миграций Django. Если вы не знакомы с миграциями — вот статья о них.

Мы рассмотрим несколько инструментов при работе с миграциями. С их помощью вы сможете полностью контролировать процесс миграций без потери данных — накатывать новые миграции и чинить старые.

Миграции — это записи в БД

Миграции — это не только файлы в папке migrations. Это ещё и таблица в базе данных. В этой таблице есть записи, какие миграции уже были выполнены, а какие — нет. Хранятся они примерно так:

Django даже позволяет туда "заглянуть":

python manage.py showmigrations

Эта команда показывает какие из миграций уже применены к БД, т.е. для каких миграций уже запускали migrate. Они помечены крестиком:

appname
 [X] 0001_initial
 [X] 0002_new_model
 [X] 0003_auto_20190805_1906
 [X] 0004_new_field
 [X] 0005_auto_20190805_1920
 [ ] 0006_auto_20190807_1545
 [ ] 0007_auto_20190808_1301
 [ ] 0008_auto_20190808_1314

Django показала все миграции, которые она нашла в папке migrations приложении appname. Крестиком помечены те, которые есть у неё в базе данных. Если вы запустите python manage.py migrate, выполнятся 3 оставшиеся миграции: 0006, 0007 и 0008.

Управление миграциями

Представим, что вам очень захотелось вернуть свой сайт к состоянию, в котором он был неделю назад. В жизни часто происходит подобное: например, неделю назад ошибки не было, а теперь есть и хочется посмотреть что было раньше. Или вы хотели использовать библиотеки, которые добавляют новые поля, а потом передумали и решили хранить всё в простом CharField.

Вернуться к старому коду очень легко, если вы используете репозиторий git. Но за время разработки модели данных тоже менялись и их также следует откатить назад вместе с кодом.

Неделю назад вы были на миграции 0005. Сейчас showmigrations показывает список из 8 миграций:

appname
 [X] 0001_initial
 [X] 0002_new_model
 [X] 0003_auto_20190805_1906
 [X] 0004_new_field
 [X] 0005_auto_20190805_1920
 [X] 0006_auto_20190807_1545
 [X] 0007_auto_20190808_1301
 [X] 0008_auto_20190808_1314

Получается, нужно отменить 3 последние миграции. Делается это одной командой:

python manage.py migrate appname 0005

Django самостоятельно удалит таблицы и столбцы в БД, которые появились в последних 3 миграциях. Если в последних миграциях таблицы удалялись, то они вернутся назад, хотя и останутся пустыми — Django может вернуть таблицы, но не может вернуть их содержимое. То же самое работает и наоборот: таблицы, которые удаляются при откатывания миграции, удаляются вместе с данными.

showmigrations теперь выдаст 3 последние миграции без крестиков:

appname
 [X] 0001_initial
 [X] 0002_new_model
 [X] 0003_auto_20190805_1906
 [X] 0004_new_field
 [X] 0005_auto_20190805_1920
 [ ] 0006_auto_20190807_1545
 [ ] 0007_auto_20190808_1301
 [ ] 0008_auto_20190808_1314

Выполнить не все миграции

Этот же инструмент позволяет запускать миграции частями, а не только все разом. Допустим, вы сейчас на пятой миграции:

 [X] 0004_new_field
 [X] 0005_auto_20190805_1920
 [ ] 0006_auto_20190807_1545
 [ ] 0007_auto_20190808_1301
 [ ] 0008_auto_20190808_1314

Теперь попробуем переместиться на седьмую миграцию:

python manage.py migrate appname 0007

Вместе с ней исполнилась и шестая миграция. Это связано с тем, что миграции расположены в "цепочке" и строго следуют друг за другом. Седьмая запускается только тогда, когда исполнены все миграции до неё:

 [X] 0004_new_field
 [X] 0005_auto_20190805_1920
 [X] 0006_auto_20190807_1545
 [X] 0007_auto_20190808_1301
 [ ] 0008_auto_20190808_1314