Django migrate: cancel last migrate

309 просмотра
0
0 Комментариев

Сделал миграцию файлом «0001_migrate.py», после вноса некоторых изменений сделал следующую «0002_migrate.py». Вопрос: как откатить последнею миграцию??


Добавить комментарий

2 Answers

Python Опубликовано 18.12.2018
0

Чтобы откатить миграцию используйте команду
python manage.py migrate
Хотя странно, почему у вас миграции одинаково называются.

Добавить комментарий
0

Миграции в django пришли из миграций South. Впервые они были представлены в версии 1.7 и работают по похожему принципу.

Все миграции располагаются в директориях приложений в своей директории migrations (создаются автоматически).

Порядок миграции в большей части определяет четыре лидирующих числа в её названии. Например, в рабочем проекте может быть что-то вроде

$./manage.py migrate core --list
core
 [X] 0001_initial
 [X] 0002_auto_20151106_1729
 [X] 0003_auto_20151110_1149
 [ ] 0004_auto_20151110_1906

Первая миграция называется i8nitial, она, как говорит название, является первичной в данном приложении т.е. создаёт начальный набор таблиц при создании приложения. Иногда initial миграций может быть несколько, обычно когда между приложениями сложные связи, которые не может покрыть одна миграция (ведь надо ещё генерировать их так, чтобы поддерживались все базы данных, а это не так просто).

Сами миграции создаются командой ./manage.py makemigrations.

У этой команды есть несколько полезных аргументов, их можно использовать вместе

  • --dry-run

    Позволяет посмотреть изменения, не генерируя при этом миграции

  • --verbosity (-v)

    Насколько подробным будет вывод команды генерации миграций. Если указать -v3, то будет выведено как будут выглядеть файлы миграций

Для просмотра списка миграций есть команда ./manage.py showmigrations (или перекочевавшая из south ./manage.py migrate --list). Те миграции, что отмечены крестиком вначале, применены к базе данных, те, что нет, соответственно, нет. Применить их можно командой ./manage.py migrate — django будет выполнять миграции в порядке, вычисленном через dependencies (рассмотрим позже)

Сам файл миграции состоит из одного класса Migration, который наследует одноимённый класс из django. Он в свою очередь состоит из двух наиболее важных для нас частей:

  • dependencies
  • operations

Сама структура, по которой django определяет порядок миграций, представляет из себя дерево зависимостей. Массив dependencies определяет после каких миграций должна выполняться текущая. Это сделано для того, чтобы можно было правильно обрабатывать ссылки на другие приложения. Например, чтобы наши миграции выполнялись после выполнения миграций django, если мы ссылаемся на структуры django (скажем, на ConetentTypes). Если бы этого не было, то сначала бы мигрировали наши приложения и получали ошибку, потому что таблиц, на которые мы ссылаемся, ещё нет.

<оффтоп> тут надо заметить, что джанга иногда тупит и эти самые связи со своими приложениями не замечает, ввиду чего надо лезть в миграции руками и вписывать в зависимости нужные приложения

Также это нужно для поддержания внутреннего состояния моделей в миграциях.

Сейчас поясню

Дело в том, что фактически у нас есть три представления наших моделей — то, что мы видим, открывая файл models.py, то, что лежит в базе, и то, что django видит в миграциях. Вообще django строит виртуальные модели при обработке каждой миграции.

Ну, например, когда у вас есть изменения в модели, а в базе они ещё не применены django перед выполнением миграций, выстроит по прошлым миграциям структуру моделей, которые отражены в базе. Это нужно для миграций данных, например, когда вам надо подменить старые данные новыми. В документации это в целом описано, даже примерчик есть

def forwards_func(apps, schema_editor):
    # Получаем модель из app, который является текущим видом моделей
    # если импортировать её напрямую, то у нас будет не та версия, что в базе
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias)

Вот тут django получает виртуальную модель через get_model в текущем её состоянии (по идее в том, какой у нас есть в базе) и уже с ней предлагает нам работать

Технически django импортирует каждый файл миграций из каждого приложения и ищет в нём класс Migration. Потом строит дерево, по которому вычисляет очерёдность миграций. Последним шагом является последовательное выполнение всех неприменённых миграций

Надо заметить, что мигрировать можно как вперёд, так и назад, указывая команде ./manage.py migrate приложение и название миграции в данном приложении. Полное название можно не указывать, достаточно цифр. Например, ./manage.py migrate core 0002. Для полного удаления всех миграций в приложении (вместе с таблицами) есть специальное зарезервированное слово zero, его надо указать вместо номера миграции.

Далее в классе миграции идёт массив операций — operations. В нём содержатся все операции, которые django последовательно выполняет при миграции вперёд и в обратном направлении при откате миграций.

Данные операции бывают нескольких видов. Все они описаны в документации, их можно разбить на несколько типов.

Обычные операции, такие как CreateModel, RenameField — в них указываются параметры соответственно какие модели изменять и что собственно изменять/создавать/и т.д.

RunSQL — это обычный SQL. Бывает нужно при сложных запросах или когда надо сделатьчто-то нестандартное. Например, сбросить счётчик автоинкримента на столбце.

RunPython — это обычный вызов функции python при выполнении миграции. В параметры передаётся две функции — одна что делать когда мигрируем вперёд, другая когда откатываем изменения. Ничего сложного, нужно только помнить про то, что некоторые бд (mysql, например) в случае ошибки в python коде ничего вам назад не откатят.

SeparateDatabaseAndState — это очень мощный инструмент, но не для новичков. Позволяет оперировать внутренними структурами мигратора, например, перенести таблицу из одного приложения в другое вместе со всеми внутренними структурами. Честно я до конца с ним не разобрался, не буду расписывать

Ну и надо ещё упомянуть, что миграции бывают двух типов — миграции схемы данных (schema migrations) и миграции данных (data migrations). Одни изменяют структуру, другие данные. В принципе в зависимости от бд можно их смешивать. К сожалению я мало работал с чем-то отличным от postgres, поэтому примеров из личного опыта не приведу, тут можно смешивать.

Есть ещё интересная возможность миграций squash migrations. Нужна, если у вас количество миграций перевалило за несколько сотен, вам это надоело и вы решили, что все новые базы должны создаваться не последовательным выполнением всех миграций, а сразу перескакивать на последнюю. При этом все клиенты, которые мигрировали до squash’а будут нормально мигрировать до неё, а все новые будут сразу перескакивать на последнюю.

По опыту очень недопиленная штука, в больших приложениях бывает проще снести все миграции и сделать ./manage.py migrate --fake — эта команда позволяет мигрировать не по настоящему — в базу будет записано, что всё мигрировано, а на самом деле никаких изменений произведено не будет.

Всё остальное в миграциях бывает нужно крайне редко. Надеюсь хоть немного прояснил ситуацию

Добавить комментарий
Напишите свой ответ на данный вопрос.
Scroll Up