Отмена дата-миграций
Помимо обычных миграций есть ещё дата-миграции. Они не меняют структуру таблиц в БД, но изменяют данные в БД.
Ниже приведён пример дата-миграции на сайте с блогом. У поста есть два поля: author
и owner
, перед вами стоит задача перенести данные из старого поля owner
в новое author
. Именно это делает функция move_owners_to_authors
:
from django.db import migrations
def move_owners_to_authors(apps, schema_editor):
Post = apps.get_model('blog', 'Post')
for post in Post.objects.all():
post.author = post.owner
post.owner = None
post.save()
class Migration(migrations.Migration):
dependencies = [
('appname', '0007_auto_20190808_1301'),
]
operations = [
migrations.RunPython(move_owners_to_authors),
]
При создании обычных миграций (их называют схема-миграции) создаются как бы две миграции сразу. Одна меняет схему таблиц, а другая — зеркальная, описывает как вернуть всё назад. Схема-миграции обычно создаются автоматически с помощью makemigrations
и у них всегда есть зеркальные копии, в то время как дата-миграции пишут разработчики самостоятельно и лишний код они писать не хотят, поэтому зеркальных копий у дата-миграций часто нет. Более того, обратную миграцию написать не всегда возможно. Если вы пытаетесь отменить дата-миграцию, у которой нет “зеркальной” миграции, то она выдаст ошибку:
$ python manage.py migrate appname 0007
django.db.migrations.exceptions.IrreversibleError: Operation
<RunPython <function move_owners_to_authors at 0x7f4822c7e950>>
in appname.0008_auto_20190808_1314 is not reversible
Чтобы отменить дата-миграцию нужна вторая функция move_backward
, которая вернёт всё назад. Она передаётся вторым аргументом в RunPython
в самом конце файла:
from django.db import migrations
def move_owners_to_authors(apps, schema_editor):
Post = apps.get_model('blog', 'Post')
for post in Post.objects.all():
post.author = post.owner
post.owner = None
post.save()
def move_backward(apps, schema_editor):
Post = apps.get_model('blog', 'Post')
for post in Post.objects.all():
post.owner = post.author
post.author = None
post.save()
class Migration(migrations.Migration):
dependencies = [
('appname', '0007_auto_20190808_1301'),
]
operations = [
migrations.RunPython(move_owners_to_authors, move_backward),
]
Повторный запуск дата-миграций
Обычно код дата-миграций подходит для многократного запуска, что очень помогает в отладке: дата-миграцию можно запускать раз за разом, пока та не заработает как надо. Но этому сопротивляется команда migrate
— она не дает повторно запустить однажды выполненную миграцию. Чтобы “обмануть” migrate
можно просто стереть из БД запись о запуске последней миграции:
$ python manage.py migrate --fake appname 0007
Rendering model states... DONE
Unapplying appname.0008_auto_20190808_1314... FAKED
Опция --fake
запускает миграций в “фейковом” режиме: удаляет из БД запись о том, что миграция 0008
раньше уже применялась, но пропускает запуск самого файла с миграцией 0008
:
[X] 0006_auto_20190807_1545
[X] 0007_auto_20190808_1301
[ ] 0008_auto_20190808_1314
Теперь вы можете переписать код миграции 0008
и запустить её заново:
$ python manage.py migrate
Running migrations:
Applying appname.0008_auto_20190808_1314... DONE
$ python manage.py showmigrations
...
[X] 0006_auto_20190807_1545
[X] 0007_auto_20190808_1301
[X] 0008_auto_20190808_1314
Чтобы этот прием эффективно работал миграция должна подходить для повторного запуска. Как этого добиться описано в статье про идемпотентные дата-миграции.
Подробнее о --fake
Благодаря --fake
можно безнаказанно стирать из БД записи о миграциях. Но --fake
опасен: если вы создали модель, затем отменили эту миграцию с помощью --fake
, то запись о выполненной миграция пропадёт, но таблица в БД для этой модели останется. Схема база данных и миграции окажутся рассинхронизированными, и когда вы попытаетесь вновь создать таблицу, то получите ошибку: невозможно создать эту модель, так как она уже есть в базе данных.
Опция --fake
чрезвычайно удобна при написании дата-миграций: если вам не понравилась какая-то дата-миграция, вы можете сделать вид, что её не было, переписать, и снова запустить. Это позволит не плодить сотни дата-миграций с исправлениями старой логики, а только поправлять один файл миграции и запускать его раз за разом.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.