django что такое миграции
Документация Django 1.7
Django использует миграции для переноса изменений в моделях (добавление поля, удаление модели и т.д.) на структуру базы данных. Миграции создавались в основном для автоматической работы, но вам необходимо знать когда их создавать, запускать и как решать различные проблемы.
Краткая история¶
До версии 1.7, Django позволял только добавлять новые модели в базу данных; не было возможности изменять или удалять существующие модели, используя команду syncdb (предок команды migrate ).
Сторонние инструменты, самый известный South, позволяли создавать и выполнять миграции. Со временем было решено перенести этот функционал в Django.
Две команды¶
Django предоставляет две команды для работы с миграциями и структурой базы данных:
Файлы с миграциями находятся в каталоге “migrations” приложения. Они являются частью приложения и должны распространятся вместе с остальным кодом приложения. Они должны создаваться при разработке и потом применятся на машинах коллег, тестовом и “боевом” серверах.
Миграции работают идентично на одном и том же наборе данных. Это означает, что на сервере разработке, тестовом и “боевом” серверах вы получите один и тот же результат при одинаковых условиях выполнения миграций.
Поддержка бэкендами¶
Миграции поддерживаются всеми бэкендами, которые предоставляет Django, как и сторонними, если они реализуют API внесения изменений в структуру базы данных (через класс SchemaEditor).
Тем не менее, некоторые базы данных поддерживают больше возможностей, чем другие, в случаях когда речь идёт о миграциях схемы. Некоторые ограничения будут описаны далее.
PostgreSQL¶
PostgreSQL предоставляет больше всего возможностей для миграций структуры данных. Единственное ограничение в том, что добавление столбцов со значениями по умолчанию вызывает полную перезапись таблицы и требует времени, пропорциональное её размеру.
MySQL¶
В MySQL отсутствует поддержка транзакций при изменении структуры. Это означает, что если миграция не выполнится из-за ошибки вам придется вручную откатывать изменения, чтобы попытаться снова (т.к. невозможно вернуться к исходному состоянию автоматически).
Кроме того, MySQL будет полностью переписать таблицы почти для каждой операции изменения структуры, это занимает время, пропорциональное количеству строк в таблице. На медленном оборудовании это может занимать больше, чем минута на миллион строк. Добавление нескольких столбцов в таблицу с всего несколькими миллионами строк может заблокировать ваш сайт на более чем десять минут.
Наконец, MySQL имеет достаточно малый предел для длины имен столбцов, таблиц и индексов, а также ограничение на общий размер всех столбцов и индексов. Это означает, что индексы, которые могут создать другие бэкенды, не будут созданы MySQL.
SQLite¶
SQLite очень плохо поддерживает изменения в структуре базы данных, но Django пытается эмулировать их следующим образом:
Создание новой таблицы для новой структуры
Копирование данных в новую таблицу
Удаление старой таблицы
Переименование новой таблицы
Этот процесс как правило хорошо работает, но может быть медленным и иногда глючит. Не рекомендуется использовать и мигрировать SQLite на “боевом” сервере, если вы не очень осведомлены о рисках и его ограничениях. Django поддерживает SQLite, чтобы позволить разработчикам использовать SQLite для разработки простых проектов.
Работа с миграциями¶
Создав новые миграции, вам следует применить их к вашей базе данных, чтобы убедиться, что всё работает:
Команда работает в два этапа. Сначала она синхронизирует приложения без миграций (по сути выполняет действия аналогичные syncdb ), а затем применяются миграции, которые ещё не применялись к базе данных.
Контроль версий¶
Поскольку миграции хранятся в системе контроля версий, вы будете иногда сталкиваться с ситуациями, когда вы и другой разработчик одновременно добавили миграции для одного приложения, в результате чего появилось две миграции с одним и тем же номером.
Зависимости¶
Такое поведение зависимостей влияет на большинство миграционных операций, которые вы ограничили рамками одного приложения. Подобное ограничение (в makemigrations или migrate ) было отличным обещанием, но не гарантией. Любое другое приложение, которое потребуется для удовлетворения зависимости, будет использовано автоматически.
Обратите внимание, приложения без миграций не могут зависеть от приложений с миграциями. Это означает, что приложение без миграций не могут содержать ForeignKey или ManyToManyField на приложение с миграциями, в некоторых случаях это будет работать, но скорее всего вы получите ошибку.
Файлы с миграциями¶
Миграции сохраняются в так называемых “файлах миграции”. Это обычные Python файлы с классом миграции, который соблюдает определенный интерфейс. В нём декларативно можно описать все необходимые операции и прочее.
Базовый файл миграции выглядит следующим образом:
Скорее всего вам никогда не понадобится менять сгенерированные миграции, но при необходимости вы можете сами написать миграцию. Некоторые сложные изменения Django не может автоматически определить, в таких случаях их необходимо описать самостоятельно. Но не бойтесь, это несложно.
Собственные поля¶
Добавление миграций в приложение¶
Если ваше приложение уже содержит модели и таблицы в базе данных, но не содержит миграции (например, вы используете приложение для предыдущих версий Django), вам необходимо преобразовать, чтобы использовать миграции. Это просто:
Обратите внимание, это работает только при соблюдении следующих условий:
Вы не меняли модели после создания таблиц в базе данных. Чтобы миграции сработали, необходимо сначала создать начальную миграцию, а потом вносить изменения, т.к. Django определяет изменения по файлам миграций, а не структуре в базе данных.
Вы не меняли самостоятельно структуру базы данных. Django не сможет определить, что структура базы данных не соответствует структуре ваших моделей. Скорее всего вы получите ошибку при выполнении миграций.
“Исторические” модели¶
Т.к. невозможно сериализовать произвольный код Python, эти версии моделей не будут содержать методы или менеджеры, который вы добавили в модели. Однако, модели будут содержать аналогичные поля, связи и параметры Meta (с учетом версии модели, так что они могут отличаться от моделей, которые находятся на данный момент в приложении).
Ссылки на функции, которые используются в параметрах поля (например, upload_to и limit_choices_to ), будут сохранены в миграциях. Такие функции нельзя удалять из кода проекта, пока миграции ссылаются на них.
В дополнение, родительские классы модели также сохраняются в миграциях в виде указателей. Их необходимо сохранять, пока существует миграция, которая ссылается на них. Но в качестве бонуса у вас будет доступ ко всем методам и менеджерам из родительских классов. Так что, если вам действительно необходим доступ в миграциях к собственным методам и менеджерам, вы можете вынести их в родительский класс.
Миграция данных¶
Вы можете использовать миграции не только для изменения структуры базы данных, но и для изменения данных. Можно и в контексте изменения структуры, чтобы мигрировать данные на новую структуру таблицы.
Миграции, которые изменяют данные, обычно называют “миграциями данных”. Их лучше выносить в отдельную миграцию.
Для начала создайте пустую миграцию (Django создаст файл миграции, положит его в правильно место, создаст название и добавит необходимые зависимости):
Теперь откройте файл, он будет выглядеть следующим образом:
Теперь выполним python manage.py migrate и миграция данных будет применена вместе с остальными миграциями.
Если вы хотите узнать как писать более сложные миграции, или как создавать миграции с нуля, смотрите раздел об операциях в миграциях.
Объединение миграций¶
Вы можете создавать сколько угодно миграций и не задумываться об их количестве. Код миграций оптимизирован и может работать с сотнями миграций за вменяемое время. Но, если вы хотите уменьшить количество миграций, вы можете объединить их.
Объединение позволяет сократить количество миграций до одной (или нескольких), которая будет выполнять аналогичные изменения структуры.
Новые файлы миграций содержат пометку, что они созданы на основе существующих миграций, таким образом они могут существовать и использоваться параллельно со старыми миграциями. Django самостоятельно переключится между этими миграциями, учитывая в какой точке истории миграций вы сейчас находитесь. Если вы применили только часть миграций, которые были объединены, Django будет использовать старые миграции, пока не выполнит их, затем переключится на новую историю. Для новой базы данных сразу будут использоваться новые миграции, старые миграции будут пропущены.
Это позволяет объединить миграции и не сломать проект на сервере, который ещё не обновили до последней версии. Рекомендуем следующую последовательность действий: объединяем миграции, старые не удаляем, коммитим и обновляем все сервера при следующем релизе (если это распространяемое приложение, убеждаемся, что все пользователи обновились до последней версии), удаляем старые миграции, коммитим и обновляем сервера до последней релизной версии.
После объединения миграций закоммитьте их, не удаляя старые миграции. После этого обновите все установленные версии проекта.
После этого объединенную миграцию можно преобразовать в обычную начальную миграцию:
Удалите все миграции, которые она заменяет
Удалите аргумент replaces в классе Migration объединенной миграции (он указывает Django, что это объединенная миграция)
Объединив миграции в новую, вы не можете снова использовать её при новом объединении, пока она не будет преобразована в обычную миграцию.
Сериализация значений¶
Django позволяет сериализовать следующее:
Ссылку на любую функцию или метод (например datetime.datetime.today )
Любой объект с методом deconstruct() (смотрите ниже)
Django может сериализовать следующее только в Python 3:
Непривязанные методы в теле класса (смотрите ниже)
Django не может сериализовать:
Экземпляры произвольного класса (например MyClass(4.3, 5.7) )
Т.к. __qualname__ был добавлен только в Python 3, Django может сериализовать следующее только в Python 3 (непривязанный метод в теле класса), но не может в Python 2:
Для Python 2 мы рекомендуем вынести методы, которые используются как значения для upload_to или других подобных параметров, принимающих функции (например default ), в тело модуля.
Метод deconstruct()¶
Метод deconstruct() собственных полей немного отличается, он должен возвращать кортеж из четырех элементов.
Django запишет полученное значение, как инициализацию для всего класса с заданными аргументами, аналогично тому, как это делается для встроенных полей.
Если все аргументы вашего класса сериализируемые, то для автоматического создания метода вы можете использовать декоратор класса @deconstructible из django.utils.deconstruct :
Обновление с South¶
Если вы уже используете South для миграций, то обновиться до django.db.migrations будет просто:
Убедитесь, что все установки проекта используют последнюю версию миграций
Это всё! Единственной проблемой могут стать циклические внешние ключи. В этом случае makemigrations может создать несколько начальных миграций и вам необходимо самостоятельно пометить их как выполненные:
Сторонние библиотеки и приложения¶
Если вы разрабатываете библиотеку или приложение и хотите поддерживать как миграции South (для Django 1.6 и ниже) так и Django (для 1.7 и выше), вам необходимо предоставлять параллельно два набора миграций в вашем приложении.
Документация Django 3.0
Django использует миграции для переноса изменений в моделях (добавление поля, удаление модели и т.д.) на структуру базы данных. Миграции создавались в основном для автоматической работы, но вам необходимо знать когда их создавать, запускать и как решать различные проблемы.
Команды¶
Django предоставляет две команды для работы с миграциями и структурой базы данных:
Файлы с миграциями находятся в каталоге «migrations» приложения. Они являются частью приложения и должны распространятся вместе с остальным кодом приложения. Они должны создаваться при разработке и потом применятся на машинах коллег, тестовом и «боевом» серверах.
Миграции работают идентично на одном и том же наборе данных. Это означает, что на сервере разработки, тестовом и «боевом» серверах вы получите один и тот же результат при одинаковых условиях выполнения миграций.
Поддержка бэкендами¶
Миграции поддерживаются всеми бэкендами, которые предоставляет Django, как и сторонними, если они реализуют API внесения изменений в структуру базы данных (через класс SchemaEditor ).
Тем не менее, некоторые базы данных поддерживают больше возможностей, чем другие, в случаях когда речь идёт о миграциях схемы. Некоторые ограничения будут описаны далее.
PostgreSQL¶
PostgreSQL is the most capable of all the databases here in terms of schema support.
MySQL¶
В MySQL отсутствует поддержка транзакций при изменении структуры. Это означает, что если миграция не выполнится из-за ошибки вам придется вручную откатывать изменения, чтобы попытаться снова (т.к. невозможно вернуться к исходному состоянию автоматически).
Кроме того, MySQL будет полностью переписывать таблицы почти для каждой операции изменения структуры, это занимает время, пропорциональное количеству строк в таблице. На медленном оборудовании это может занимать больше, чем минута на миллион строк. Добавление нескольких столбцов в таблицу с всего несколькими миллионами строк может заблокировать ваш сайт на более чем десять минут.
Finally, MySQL has relatively small limits on name lengths for columns, tables and indexes, as well as a limit on the combined size of all columns an index covers. This means that indexes that are possible on other backends will fail to be created under MySQL.
SQLite¶
SQLite очень плохо поддерживает изменения в структуре базы данных, но Django пытается эмулировать их следующим образом:
Этот процесс как правило хорошо работает, но может быть медленным и иногда глючит. Не рекомендуется использовать и мигрировать SQLite на «боевом» сервере, если вы не очень осведомлены о рисках и его ограничениях. Django поддерживает SQLite, чтобы позволить разработчикам использовать SQLite для разработки простых проектов.
Работа с миграциями¶
Создав новые миграции, вам следует применить их к вашей базе данных, чтобы убедиться, что всё работает:
Контроль версий¶
Поскольку миграции хранятся в системе контроля версий, вы будете иногда сталкиваться с ситуациями, когда вы и другой разработчик одновременно добавили миграции для одного приложения, в результате чего появилось две миграции с одним и тем же номером.
Зависимости¶
Такое поведение зависимостей влияет на большинство миграционных операций, которые вы ограничили рамками одного приложения. Подобное ограничение (в makemigrations или migrate ) было отличным обещанием, но не гарантией. Любое другое приложение, которое потребуется для удовлетворения зависимости, будет использовано автоматически.
Файлы с миграциями¶
Migrations are stored as an on-disk format, referred to here as «migration files». These files are actually normal Python files with an agreed-upon object layout, written in a declarative style.
Базовый файл миграции выглядит следующим образом:
Скорее всего вам никогда не понадобится менять сгенерированные миграции, но при необходимости вы можете сами написать миграцию. Некоторые сложные изменения Django не может автоматически определить, в таких случаях их необходимо описать самостоятельно. Но не бойтесь, это несложно.
Собственные поля¶
Менеджеры модели¶
Если вы используете функцию :meth: `
django.db.models.from_queryset`для создания менеджера, вам следует унаследоваться от сгенерированного класса:
Начальные миграции¶
The «initial migrations» for an app are the migrations that create the first version of that app’s tables. Usually an app will have one initial migration, but in some cases of complex model interdependencies it may have two or more.
Начальные миграции помечаются атрибутом initial = True в классе миграции. Если атрибут initial не указан, миграция будет считаться «начальной», если это первая миграция в приложении (то есть она не зависит от другой миграции текущего приложения).
History consistency¶
As previously discussed, you may need to linearize migrations manually when two development branches are joined. While editing migration dependencies, you can inadvertently create an inconsistent history state where a migration has been applied but some of its dependencies haven’t. This is a strong indication that the dependencies are incorrect, so Django will refuse to run migrations or make new migrations until it’s fixed. When using multiple databases, you can use the allow_migrate() method of database routers to control which databases makemigrations checks for consistent history.
Добавление миграций в приложение¶
New apps come preconfigured to accept migrations, and so you can add migrations by running makemigrations once you’ve made some changes.
If your app already has models and database tables, and doesn’t have migrations yet (for example, you created it against a previous Django version), you’ll need to convert it to use migrations by running:
Обратите внимание, это работает только при соблюдении следующих условий:
Reverting migrations¶
Any migration can be reverted with migrate by using the number of previous migrations:
If you want to revert all migrations applied for an app, use the name zero :
«Исторические» модели¶
When you run migrations, Django is working from historical versions of your models stored in the migration files. If you write Python code using the RunPython operation, or if you have allow_migrate methods on your database routers, you need to use these historical model versions rather than importing them directly.
If you import models directly rather than using the historical models, your migrations may work initially but will fail in the future when you try to re-run old migrations (commonly, when you set up a new installation and run through all the migrations to set up the database).
This means that historical model problems may not be immediately obvious. If you run into this kind of failure, it’s OK to edit the migration to use the historical models rather than direct imports and commit those changes.
Т.к. невозможно сериализовать произвольный код Python, эти версии моделей не будут содержать методы, который вы добавили в модели. Однако, модели будут содержать аналогичные поля, связи, менеджеры(только те, которые содержат use_in_migrations = True ) и параметры Meta (с учетом версии модели, так что они могут отличаться от моделей, которые находятся на данный момент в приложении).
Ссылки на функции, которые используются в параметрах поля (например, upload_to и limit_choices_to ), и менеджеры моделей с use_in_migrations = True будут сериализированы в миграциях. Такие функции нельзя удалять из кода проекта, пока существуют миграции, которые ссылаются на них. Все собственные поля моделей также должы быть доступны т.к. они явно импортируются в миграциях.
In addition, the concrete base classes of the model are stored as pointers, so you must always keep base classes around for as long as there is a migration that contains a reference to them. On the plus side, methods and managers from these base classes inherit normally, so if you absolutely need access to these you can opt to move them into a superclass.
To remove old references, you can squash migrations or, if there aren’t many references, copy them into the migration files.
Советы по удалению полей модели¶
Из предыдущего раздела вы узнали, что удаление собственных полей модели из проекта или используемых приложений может привести к проблемам с миграциями, если существуют миграции, которые ссылаются на эти поля.
Добавьте атрибут system_check_deprecated_details к полю модели:
После определенного периода поддержки устаревшего кода (Django использует три мажорных релиза для своих полей), замените system_check_deprecated_details на system_check_removed_details со следующим значением:
Миграция данных¶
Вы можете использовать миграции не только для изменения структуры базы данных, но и для изменения данных. Можно и в контексте изменения структуры, чтобы мигрировать данные на новую структуру таблицы.
Миграции, которые изменяют данные, обычно называют «миграциями данных». Их лучше выносить в отдельную миграцию.
Для начала создайте пустую миграцию (Django создаст файл миграции, положит его в правильно место, создаст название и добавит необходимые зависимости):
Теперь откройте файл, он будет выглядеть следующим образом:
Let’s write a migration that populates our new name field with the combined values of first_name and last_name (we’ve come to our senses and realized that not everyone has first and last names). All we need to do is use the historical model and iterate over the rows:
Once that’s done, we can run python manage.py migrate as normal and the data migration will run in place alongside other migrations.
Вы можете передать вторым аргументом RunPython еще одну функцию, которая будет использоваться при отмене миграции. Если такая функция не указана, то при попытке отменить миграцию будет вызвано исключение.
Доступ к моделям в других приложениях¶
Миграции для продвинутых¶
Объединение миграций¶
Вы можете создавать сколько угодно миграций и не задумываться об их количестве. Код миграций оптимизирован и может работать с сотнями миграций за вменяемое время. Но, если вы хотите уменьшить количество миграций, вы можете объединить их.
Объединение позволяет сократить количество миграций до одной (или нескольких), которая будет выполнять аналогичные изменения структуры.
These files are marked to say they replace the previously-squashed migrations, so they can coexist with the old migration files, and Django will intelligently switch between them depending where you are in the history. If you’re still part-way through the set of migrations that you squashed, it will keep using them until it hits the end and then switch to the squashed history, while new installs will use the new squashed migration and skip all the old ones.
This enables you to squash and not mess up systems currently in production that aren’t fully up-to-date yet. The recommended process is to squash, keeping the old files, commit and release, wait until all systems are upgraded with the new release (or if you’re a third-party project, ensure your users upgrade releases in order without skipping any), and then remove the old files, commit and do a second release.
После объединения миграций закоммитьте их, не удаляя старые миграции. После этого обновите все установленные версии проекта.
You must then transition the squashed migration to a normal migration by:
Объединив миграции в новую, вы не можете снова использовать её при новом объединении, пока она не будет преобразована в обычную миграцию.
Сериализация значений¶
Django позволяет сериализовать следующее:
Serialization support for NoneType was added.
Django не может сериализовать:
Custom serializers¶
You can serialize other types by writing a custom serializer. For example, if Django didn’t serialize Decimal by default, you could do this:
The first argument of MigrationWriter.register_serializer() is a type or iterable of types that should use the serializer.
The serialize() method of your serializer must return a string of how the value should appear in migrations and a set of any imports that are needed in the migration.
Adding a deconstruct() method¶
Метод deconstruct() собственных полей немного отличается, он должен возвращать кортеж из четырех элементов.
Django запишет полученное значение, как инициализацию для всего класса с заданными аргументами, аналогично тому, как это делается для встроенных полей.
Поддержка нескольких версий Django¶
Если вы разрабатываете распространяемое приложение с моделями, вам необходимо создать миграции, которые поддерживают различные версии Django. В этом случае вы должны всегда выполнять makemigrations для самый старой версии Django, которую вы поддерживаете.
Система миграций поддерживает обратную совместимость как и остальная часть Django, и файлы миграций для Django X.Y должны работать для Django X.Y+1. Однако, система миграций не гарантирует обратную совместимость. Могут добавляться новые возможности и новые миграции не будут работать на старой версии Django.
The Migrations Operations Reference Описывает API операций для изменений структуры базы данных, специальные операции, и создания своих операций. The Writing Migrations «how-to» Описывает как самостоятельно создать миграции для решения различных ситуаций.