Django ORM: Миграции как "снимки" Системы
(речь пойдет о миграциях в Django 1.7 и выше)
Для начала некоторые факты о миграциях:
●
Миграции предназначены для программирования изменений в базах данных проекта. Это могут быть как изменения в схеме данных (изменение структуры таблиц, ограничений целостности, индексов и т.п.), так и изменения в самих данных (исправление ошибочных данных, загрузка новых данных и т.п.).
●
Каждая миграция применяется для
каждой БД проекта (баз данных может быть несколько).
● Миграция состоит из т.н.
операций. Операции могут быть как встроенные, например, создание таблицы для модели, добавление/изменение/удаление/переименование поля в таблице и т.п., так и реализованные в рамках сторонних библиотек, либо проекта.
● Операция имеет
"направление" выполнения. Прямое (forwards) реализуется всегда, обратное (backwards) при наличии такой возможности/необходимости.
● Выполнение или пропуск операции определяется через
роутеры в методе allow_migrate.
● Перед и после выполнения миграций каждого django-приложения отправляются сигналы pre_migrate и post_migrate.
● Каждая миграция по умолчанию выполняется в отдельной транзакции. Поэтому надо учитывать некоторые особенности, обусловенные этим. Например, внутри транзакций нельзя выполнять некоторые операции (зависит от СУБД и её версии). Это определяется атрибутом atomic в классе миграции.
● Для удобства в каждом приложении миграции нумеруются. Но порядок применения миграций определяется через
зависимости, а не их номерами. Например, зависимости можно описать так, что сначала будет применена миграция 0001, затем 0004, потом 0002 и только после этого 0003.
● Зависимости могут быть определены через атрибуты dependencies и run_before. В них указываются миграции, которые нужно выполнить до/после данной миграции соответственно.
Миграции — это снимки (snapshot) Системы
При необходимости выполнения в операциях миграций действий с моделями (
CRUD) в Django используются т.н. исторические модели. Иными словами, это снимки моделей Системы на момент реализации миграции. Это обусловлено тем, что модели Системы не только могут быть изменены в процессе её развития, но и вовсе удалены, однако операция должна всё равно выполняться при применении миграций с нуля (например, при запуске автотестов).
Отсюда следует другой не всегда очевидный факт: помимо моделей в Системе могут меняться и другие её составные части, например, константы, функции, классы и т.п. Поэтому при написании нестандартных миграций целесообразно также делать "снимок" используемых в миграции объектов, которые в будущем могут быть изменены, т.е. копипастить. Пожалуй, это единственный случай, когда
программирование копипастом не является антипаттерном 🙂
Исторические модели — это самостоятельные классы!
При написании миграций следует помнить, что историческая модель не имеет ничего общего с моделью Системы, кроме, разве что, имени. Это означает, что атрибуты и методы, имеющиеся в моделях Системы, отсутствуют в исторических моделях. Обработчики сигналов, подключенные с параметром sender также не будут срабатывать на исторические модели.