Миграции
Миграции — это способ Django распространять изменения, которые вы вносите в свои модели (добавление поля, удаление модели и т. д.) в схему вашей базы данных. Они разработаны, чтобы быть в основном автоматическими, но вам нужно знать, когда создавать миграции, когда их запускать и какие распространённые проблемы могут возникнуть.
Краткая история
До версии 1.7 Django поддерживал только добавление новых моделей в базу данных; изменить или удалить существующие модели через команду syncdb (предшественник migrate) было невозможно.
Инструменты сторонних разработчиков, в первую очередь South, обеспечивали поддержку этих дополнительных типов изменений, но это считалось достаточно важным, чтобы данная поддержка была включена в основной Django.
Команды
Существует несколько команд, которые вы будете использовать для взаимодействия с миграциями и обработкой Django схем баз данных:
-
migrate, которая отвечает за применение миграций, а также за отмену и вывод их статуса. -
makemigrations, которая отвечает за создание новых миграций на основе изменений, которые вы внесли в свои модели. -
sqlmigrate, которая отображает SQL-запросы для миграции.
Стоит отметить, что миграции создаются и выполняются на уровне каждого приложения. В частности, есть приложения, которые не используют миграции (они называются «немигрируемыми приложениями»). Эти приложения будут имитировать устаревшее поведение, добавляя только новые модели.
Представьте миграции как систему управления версиями для вашей схемы базы данных. makemigrations отвечает за упаковку изменений в вашей модели в отдельные файлы миграций — аналогично коммитам — и migrate отвечает за их применение к вашей базе данных.
Файлы миграций для каждого приложения находятся в каталоге «migrations» внутри этого приложения и предназначены для коммита и распространения в составе его кодовой базы. Вы должны создавать их на своей машине разработки, а затем запускать те же миграции на машинах своих коллег, на тестовых серверах и, в конечном итоге, на производственных серверах.
Примечание
Можно переопределить имя пакета, содержащего миграции, на уровне каждого приложения, изменив настройку MIGRATION_MODULES.
Миграции будут работать одинаково на одних и тех же данных и дадут согласованные результаты, что означает, что то, что вы видите на этапе разработки и тестирования, в тех же условиях будет происходить и на этапе производства.
Django будет создавать миграции для любых изменений в ваших моделях или полях — даже для опций, которые не влияют на базу данных — поскольку единственный способ правильно восстановить поле — иметь все изменения в истории, а вам могут понадобиться эти опции в некоторых миграциях данных позже (например, если вы установили пользовательские валидаторы).
Поддержка бэкендов
Миграции поддерживаются всеми бэкендами, которые поставляются с Django, а также любыми сторонними бэкендами, если они запрограммируют поддержку изменения схемы (выполняется через класс SchemaEditor).
Однако некоторые базы данных более способны к миграциям схемы, чем другие; некоторые замечания приведены ниже.
PostgreSQL
PostgreSQL является наиболее способной базой данных в данном случае с точки зрения поддержки схемы; единственное замечание заключается в том, что добавление столбцов со значениями по умолчанию приведёт к полному переписыванию таблицы, пропорционально её размеру.
По этой причине рекомендуется всегда создавать новые столбцы с null=True, так как таким образом они будут добавлены сразу.
MySQL
MySQL не поддерживает транзакции в операциях изменения схемы, что означает, что если миграция не применится, вам придётся вручную отменить изменения, чтобы попробовать снова (вернуться к более ранней точке невозможно).
Кроме того, MySQL будет полностью переписывать таблицы для почти каждой операции со схемой и, как правило, занимает время, пропорциональное количеству строк в таблице для добавления или удаления столбцов. На медленном оборудовании это может быть хуже, чем минута на миллион строк — добавление нескольких столбцов в таблицу всего с несколькими миллионами строк может заблокировать ваш сайт более чем на десять минут.
Наконец, MySQL имеет достаточно небольшие ограничения на длину имен для столбцов, таблиц и индексов, а также ограничения на общий размер всех столбцов, которые индекс охватывает. Это означает, что индексы, которые возможны в других бэкендах, не будут созданы в MySQL.
SQLite
SQLite имеет очень ограниченную встроенную поддержку изменения схемы, и поэтому Django пытается имитировать её, выполняя:
- Создание новой таблицы с новой схемой
- Копирование данных
- Удаление старой таблицы
- Переименование новой таблицы в соответствии с оригинальным именем
Этот процесс, как правило, работает хорошо, но может быть медленным и иногда глючным. Не рекомендуется запускать и мигрировать SQLite в производственной среде, если вы не очень хорошо осведомлены о рисках и ограничениях; поддержка, поставляемая Django, предназначена для того, чтобы позволить разработчикам использовать SQLite на своих локальных машинах для разработки менее сложных проектов Django без необходимости в полной базе данных.
Процесс
Работа с миграциями проста. Внесите изменения в свои модели — например, добавьте поле и удалите модель — и затем запустите makemigrations:
$ python manage.py makemigrations
Migrations for 'books':
0003_auto.py:
- Alter field author on book
Ваши модели будут просканированы и сравнены с версиями, которые в настоящее время содержатся в файлах миграций, а затем будет создан новый набор миграций. Убедитесь, что вы прочитали вывод, чтобы увидеть, что makemigrations считает, что вы изменили — он не идеален, и для сложных изменений он может не обнаружить того, что вы ожидаете.
После того, как у вас появились новые файлы миграций, вы должны применить их к своей базе данных, чтобы убедиться, что они работают должным образом:
$ python manage.py migrate Operations to perform: Synchronize unmigrated apps: sessions, admin, messages, auth, staticfiles, contenttypes Apply all migrations: books Synchronizing apps without migrations: Creating tables... Installing custom SQL... Installing indexes... Installed 0 object(s) from 0 fixture(s) Running migrations: Applying books.0003_auto... OK
Команда выполняется в двух этапах; сначала она синхронизирует немигрированные приложения (выполняя ту же функциональность, что и syncdb), а затем выполняет все миграции, которые ещё не были применены.
После применения миграции внесите коммит миграции и изменений в модели в вашу систему контроля версий как единый коммит — таким образом, когда другие разработчики (или ваши производственные серверы) возьмут код, они получат и изменения в ваших моделях, и соответствующую миграцию одновременно.
Если вы хотите дать миграции(ям) осмысленное имя вместо сгенерированного, вы можете использовать опцию --name:
$ python manage.py makemigrations --name changed_my_model your_app_label
Система управления версиями
Поскольку миграции хранятся в системе управления версиями, вы время от времени сталкиваетесь со ситуациями, когда вы и другой разработчик оба внесли коммит миграции в одно и то же приложение одновременно, в результате чего получатся две миграции с одинаковым номером.
Не волнуйтесь — номера предназначены только для справки разработчиков, Django интересует только то, что каждая миграция имеет уникальное имя. Миграции указывают, от каких других миграций они зависят — включая более ранние миграции в том же приложении — в файле, поэтому можно обнаружить, когда для одного приложения существуют две новые миграции, которые не упорядочены.
В таких случаях Django попросит вас и предоставит вам несколько вариантов. Если он посчитает это достаточно безопасным, он предложит автоматизировать упорядочивание этих двух миграций. Если нет, вам придётся самостоятельно изменить миграции — не волнуйтесь, это несложно, и более подробно это описано в Файлах миграций ниже.
Зависимости
Хотя миграции выполняются на уровне приложения, таблицы и связи, предполагаемые вашими моделями, слишком сложны, чтобы их можно было создать только для одного приложения за раз. Когда вы создаёте миграцию, которая требует выполнения чего-то ещё — например, вы добавляете ForeignKey в приложении books к приложению authors — получившаяся миграция будет содержать зависимость от миграции в authors.
Это означает, что при выполнении миграций миграция authors выполняется первой и создаёт таблицу, на которую ссылается миграция ForeignKey, а затем миграция, которая создаёт столбец ForeignKey, выполняется после и создаёт ограничение. Если этого не произойдёт, миграция попытается создать столбец ForeignKey без существования таблицы, на которую он ссылается, и ваша база данных выдаст ошибку.
Это поведение зависимостей влияет на большинство операций с миграциями, где вы ограничиваете работу одним приложением. Ограничение работы одним приложением (либо в makemigrations или migrate) является обещанием наилучших усилий, а не гарантией; любые другие приложения, которые нужны для правильной настройки зависимостей, будут использоваться.
Однако учтите, что немигрированные приложения не могут зависеть от мигрированных приложений по самой природе отсутствия миграций. Это означает, что обычно невозможно иметь немигрированное приложение, имеющее ForeignKey или ManyToManyField к мигрированному приложению; некоторые случаи могут работать, но в конечном итоге это потерпит неудачу.
Предупреждение
Даже если всё кажется работающим с немигрированными приложениями, зависящими от мигрированных приложений, Django может не сгенерировать все необходимые ограничения внешних ключей!
Это особенно заметно, если вы используете взаимозаменяемые модели (например, AUTH_USER_MODEL), поскольку каждое приложение, использующее взаимозаменяемые модели, должно иметь миграции, если вам не повезёт. Со временем всё больше и больше сторонних приложений получат миграции, но тем временем вы можете либо сами дать им миграции (используя MIGRATION_MODULES для хранения этих модулей вне модуля собственного приложения, если хотите), или оставить приложение с вашей моделью пользователя немигрированным.
Кроме того, все модели, используемые в операциях RunPython должны иметь миграции, чтобы их связи с другими моделями были должным образом созданы.
Файлы миграций
Миграции хранятся в формате на диске, здесь называемом «файлами миграций». Фактически эти файлы — это обычные Python-файлы со согласованной структурой объектов, написанные в декларативном стиле.
Файл миграции базового вида выглядит так:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("migrations", "0001_initial")]
operations = [
migrations.DeleteModel("Tribble"),
migrations.AddField("Author", "rating", models.IntegerField(default=0)),
]
То, что Django ищет при загрузке файла миграции (как Python-модуля), — это подкласс django.db.migrations.Migration с именем Migration. Затем он проверяет этот объект на наличие четырёх атрибутов, из которых большинство времени используются только два:
-
dependencies, список миграций, от которых зависит текущая. -
operations, списокOperationклассов, определяющих действия этой миграции.
Операции являются ключевым элементом; они представляют собой набор декларативных инструкций, которые сообщают Django о необходимых изменениях схемы. Django сканирует их и создает в памяти представление всех изменений схемы для всех приложений, а затем использует это представление для генерации SQL-кода, который производит изменения схемы.
Эта структура в памяти также используется для определения различий между вашими моделями и текущим состоянием ваших миграций; Django проходит по всем изменениям в порядке, выполняя их над набором моделей в памяти, чтобы получить состояние ваших моделей в последний раз, когда вы запускали makemigrations. Затем он использует эти модели для сравнения с моделями в ваших файлах models.py для определения внесенных изменений.
Вам редко, если вообще, понадобится редактировать файлы миграций вручную, но это вполне возможно, если вам нужно. Некоторые более сложные операции не могут быть автоматически обнаружены и доступны только через написанные вручную миграции, поэтому не бойтесь редактировать их, если это необходимо.
Настраиваемые поля
Вы не можете изменить количество позиционных аргументов в уже иммигрировавшем настраиваемом поле без поднятия TypeError. Старая миграция вызовет измененный метод __init__ со старой сигнатурой. Поэтому, если вам нужен новый аргумент, создайте аргумент ключевого слова и добавьте что-то вроде assert 'argument_name' in kwargs в конструктор.
Менеджеры моделей
Вы можете по желанию сериализовать менеджеры в миграции и использовать их в операциях RunPython. Это делается путем определения атрибута use_in_migrations в классе менеджера:
class MyManager(models.Manager):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
Если вы используете функцию from_queryset() для динамической генерации класса менеджера, вам нужно унаследовать от сгенерированного класса, чтобы сделать его импортируемым:
class MyManager(MyBaseManager.from_queryset(CustomQuerySet)):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
Обратитесь к примечаниям о Исторических моделях в миграциях, чтобы увидеть последствия.
Добавление миграций к приложениям
Добавление миграций к новым приложениям просто — они предварительно настроены для приема миграций, поэтому просто запустите makemigrations после внесения изменений.
Если ваше приложение уже имеет модели и таблицы базы данных, но еще не имеет миграций (например, вы создали его с помощью предыдущей версии Django), вам необходимо преобразовать его для использования миграций; это простой процесс:
$ python manage.py makemigrations your_app_label
Это создаст новую начальную миграцию для вашего приложения. Теперь запустите python
manage.py migrate --fake-initial, и Django обнаружит, что у вас есть начальная миграция и что таблицы, которые он хочет создать, уже существуют, и пометит миграцию как уже примененную. (Без флага --fake-initial команда migrate вернет ошибку, потому что таблицы, которые он хочет создать, уже существуют.)
Обратите внимание, что это работает только при соблюдении двух условий:
- Вы не изменяли свои модели с момента создания их таблиц. Для работы миграций вы должны сначала создать начальную миграцию, а затем внести изменения, так как Django сравнивает изменения с файлами миграций, а не с базой данных.
- Вы не редактировали базу данных вручную — Django не сможет определить, что ваша база данных не соответствует вашим моделям, вы просто получите ошибки, когда миграции попытаются изменить эти таблицы.
Флаг --fake-initial для migrate был добавлен. Ранее Django всегда автоматически подделывал применение начальных миграций, если обнаруживал, что таблицы существуют.
Исторические модели
При выполнении миграций Django работает с историческими версиями ваших моделей, хранящимися в файлах миграций. Если вы пишете код Python, использующий операцию RunPython, или если у вас есть методы allow_migrate в маршрутизаторах базы данных, вы будете иметь дело с этими версиями ваших моделей.
Поскольку невозможно сериализовать произвольный код Python, эти исторические модели не будут иметь каких-либо пользовательских методов, которые вы определили. Однако у них будут те же поля, отношения, менеджеры (ограниченные теми, у которых есть use_in_migrations = True) и Meta опции (также версионированные, поэтому они могут отличаться от ваших текущих).
Предупреждение
Это означает, что пользовательские методы save() не будут вызываться для объектов при доступе к ним в миграциях, и у вас не будет пользовательских конструкторов или методов экземпляров. Планируйте соответствующим образом!
Ссылки на функции в опциях полей, таких как upload_to и limit_choices_to, и объявления менеджеров моделей с менеджерами, имеющими use_in_migrations = True, сериализуются в миграциях, поэтому функции и классы должны храниться до тех пор, пока есть миграция, ссылающаяся на них. Любые настраиваемые поля моделей также должны храниться, так как они импортируются миграциями напрямую.
Кроме того, базовые классы модели хранятся только как указатели, поэтому вы должны всегда хранить базовые классы до тех пор, пока есть миграция, содержащая ссылку на них. С положительной стороны, методы и менеджеры из этих базовых классов наследуются нормально, поэтому, если вам абсолютно необходим доступ к этим данным, вы можете переместить их в суперкласс.
Учет при удалении полей модели
Аналогично соображениям по «ссылкам на исторические функции», описанным в предыдущем разделе, удаление пользовательских полей модели из вашего проекта или приложения стороннего разработчика вызовет проблему, если они ссылаются на старые миграции.
Для решения этой ситуации Django предоставляет некоторые атрибуты полей модели для помощи в устаревании полей модели с использованием фреймворка системных проверок.
Добавьте атрибут system_check_deprecated_details к вашему полю модели, аналогично следующему:
class IPAddressField(Field):
system_check_deprecated_details = {
'msg': (
'IPAddressField has been deprecated. Support for it (except '
'in historical migrations) will be removed in Django 1.9.'
),
'hint': 'Use GenericIPAddressField instead.', # optional
'id': 'fields.W900', # pick a unique ID for your field.
}
После периода устаревания по вашему выбору (две основные версии выпусков для полей в самом Django), измените атрибут system_check_deprecated_details на system_check_removed_details и обновите словарь аналогично:
class IPAddressField(Field):
system_check_removed_details = {
'msg': (
'IPAddressField has been removed except for support in '
'historical migrations.'
),
'hint': 'Use GenericIPAddressField instead.',
'id': 'fields.E900', # pick a unique ID for your field.
}
Вы должны сохранить методы поля, необходимые для его работы в миграциях базы данных, такие как __init__(), deconstruct(), и get_internal_type(). Храните это поле-заглушку до тех пор, пока существуют миграции, ссылающиеся на поле. Например, после слияния миграций и удаления старых, вы должны иметь возможность полностью удалить поле.
Миграции данных
Помимо изменения схемы базы данных, вы также можете использовать миграции для изменения данных в самой базе данных в сочетании со схемой, если это необходимо.
Миграции, изменяющие данные, обычно называются «миграциями данных»; их лучше писать как отдельные миграции, находящиеся рядом с вашими миграциями схемы.
Django не может автоматически генерировать для вас миграции данных, как он делает с миграциями схемы, но написать их несложно. Файлы миграций в Django состоят из операций, и основной операцией, которую вы используете для миграций данных, является RunPython.
Для начала создайте пустой файл миграции, с которого вы сможете работать (Django разместит файл в нужном месте, предложит имя и добавит зависимости для вас):
python manage.py makemigrations --empty yourappname
Затем откройте файл; он должен выглядеть примерно так:
# -*- coding: utf-8 -*-
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
]
Теперь все, что вам нужно сделать, это создать новую функцию и использовать RunPython для ее вызова. RunPython ожидает вызываемый объект в качестве аргумента, который принимает два аргумента: первый — это реестр приложений, в котором загружены исторические версии всех ваших моделей, соответствующие положению миграции в вашей истории, а второй — SchemaEditor, который вы можете использовать для ручного внесения изменений в схему базы данных (но будьте осторожны, выполнение этого может сбить с толку автоматический детектор миграций!)
Давайте напишем простую миграцию, которая заполняет наше новое поле name комбинированными значениями first_name и last_name (мы пришли к выводу, что не у всех есть имя и фамилия). Все, что нам нужно сделать, это использовать историческую модель и перебрать строки:
# -*- coding: utf-8 -*-
from django.db import models, migrations
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model("yourappname", "Person")
for person in Person.objects.all():
person.name = "%s %s" % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
После этого мы можем просто запустить python manage.py migrate как обычно, и миграция данных выполнится вместе с другими миграциями.
Вы можете передать второй вызываемый объект в RunPython для выполнения любого необходимого кода при обратной миграции. Если этот вызываемый объект опущен, при обратной миграции будет поднята ошибка.
Доступ к моделям из других приложений
При написании функции RunPython использующей модели из приложений, отличных от того, в котором находится миграция, атрибут dependencies миграции должен включать последнюю миграцию каждого приложения, которое вовлечено, в противном случае вы можете получить ошибку, подобную: LookupError: No installed app
with label 'myappname' при попытке получить модель в функции RunPython с помощью apps.get_model().
В следующем примере у нас есть миграция в app1, которой необходимо использовать модели в app2. Мы не заинтересованы в деталях move_m1 за исключением того факта, что ей потребуется доступ к моделям из обоих приложений. Поэтому мы добавили зависимость, которая указывает на последнюю миграцию app2:
class Migration(migrations.Migration):
dependencies = [
('app1', '0001_initial'),
# added dependency to enable using models from app2 in move_m1
('app2', '0004_foobar'),
]
operations = [
migrations.RunPython(move_m1),
]
Более продвинутые миграции
Если вас интересуют более продвинутые операции миграции или вы хотите научиться создавать свои собственные, обратитесь к справочнику по операциям миграции и руководству по созданию миграций.
Сжатие миграций
Рекомендуется свободно создавать миграции и не беспокоиться о их количестве; код миграции оптимизирован для обработки сотен миграций без заметного замедления. Однако в конечном итоге вы захотите перейти от нескольких сотен миграций к нескольким, и здесь пригодится сжатие.
Сжатие — это процесс сокращения существующего набора миграций до одной (или иногда нескольких) миграций, которые по-прежнему представляют те же изменения.
Django выполняет это, беря все ваши существующие миграции, извлекая их Operation и помещая их все в последовательность, а затем применяет оптимизатор, чтобы попытаться уменьшить длину списка — например, он знает, что CreateModel и DeleteModel взаимно компенсируют друг друга, и он знает, что AddField можно объединить с CreateModel.
После того, как последовательность операций была сокращена по возможности — степень сокращения зависит от того, насколько тесно связаны ваши модели и есть ли у вас какие-либо RunSQL или RunPython операции (которые не могут быть оптимизированы) — Django запишет их обратно в новые файлы начальных миграций.
Эти файлы помечены как заменяющие ранее сжатые миграции, поэтому они могут сосуществовать со старыми файлами миграций, и Django интеллектуально переключается между ними в зависимости от того, где вы находитесь в истории. Если вы всё ещё находитесь в процессе миграции, которую сжали, Django будет продолжать использовать их до тех пор, пока не достигнет конца, а затем переключится на сжатую историю, в то время как новые установки будут использовать только новые сжатые миграции и пропускать все старые.
Это позволяет вам сжать миграции и не сломать системы, которые в настоящее время находятся в производстве и ещё не полностью обновлены. Рекомендуемый процесс заключается в сжатии, сохранении старых файлов, добавлении изменений в историю, ожидании, пока все системы будут обновлены с новой версией (или, если вы сторонний проект, убедитесь, что ваши пользователи обновляют релизы в порядке, не пропускают ни одной), а затем удалите старые файлы, внесите изменения в историю и сделайте второе обновление.
Команда, которая реализует всё это, — это squashmigrations — просто передайте ей метку приложения и имя миграции, которую вы хотите сжать, и она приступит к работе:
$ ./manage.py squashmigrations myapp 0004 Will squash the following migrations: - 0001_initial - 0002_some_change - 0003_another_change - 0004_undo_something Do you wish to proceed? [yN] y Optimizing... Optimized from 12 operations to 7 operations. Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_somthing.py You should commit this migration but leave the old ones in place; the new migration will be used for new installs. Once you are sure all instances of the codebase have applied the migrations you squashed, you can delete them.
Обратите внимание, что взаимозависимости моделей в Django могут стать очень сложными, и сжатие может привести к миграциям, которые не работают; либо они неправильно оптимизированы (в этом случае вы можете повторить попытку с --no-optimize, хотя вам также следует сообщить об ошибке), или с CircularDependencyError, в этом случае вы можете исправить её вручную.
Чтобы вручную исправить CircularDependencyError, выделите одну из внешних зависимостей в циклической зависимости и поместите её в отдельную миграцию, а также переместите зависимость от другого приложения вместе с ней. Если вы не уверены, посмотрите, как makemigrations справляется с проблемой, когда её просят создать новые миграции из ваших моделей. В будущих версиях Django squashmigrations будет обновлён для попытки исправить эти ошибки самостоятельно.
После сжатия миграции вы должны внести её в историю вместе с миграциями, которые она заменяет, и распространить это изменение на все работающие экземпляры вашего приложения, убедившись, что они выполнили migrate для сохранения изменений в своей базе данных.
После этого вы должны преобразовать сжатую миграцию в обычную начальную миграцию, выполнив:
- Удаление всех файлов миграций, которые она заменяет
- Удаление аргумента
replacesв классеMigrationсжатой миграции (так Django определяет, что это сжатая миграция)
Примечание
После сжатия миграции не следует повторно сжимать эту сжатую миграцию до тех пор, пока она не будет полностью преобразована в обычную миграцию.
Сериализация значений
Миграции — это просто файлы Python, содержащие старые определения ваших моделей; следовательно, для их записи Django должен получить текущее состояние ваших моделей и сериализовать их в файл.
Хотя Django может сериализовать большинство вещей, некоторые вещи мы просто не можем сериализовать в допустимое представление Python — нет стандартного Python-метода, позволяющего преобразовать значение обратно в код (repr() работает только для базовых значений и не указывает пути импорта).
Django может сериализовать следующее:
-
int,long,float,bool,str,unicode,bytes,None -
list,set,tuple,dict -
datetime.date,datetime.time, и экземплярыdatetime.datetime(включая те, которые учитывают часовой пояс) -
decimal.Decimalэкземпляры - Любое поле Django
- Любая ссылка на функцию или метод (например,
datetime.datetime.today) (должна быть в верхнем уровне модуля) - Любая ссылка на класс (должна быть в верхнем уровне модуля)
- Всё, что имеет пользовательский метод
deconstruct()(см. ниже)
Добавлена поддержка сериализации даты и времени с учётом часового пояса.
Django может сериализовать следующее только на Python 3:
- Несвязанные методы, используемые в теле класса (см. ниже)
Django не может сериализовать:
- Вложенные классы
- Произвольные экземпляры класса (например,
MyClass(4.3, 5.7)) - Lambda-функции
Поскольку __qualname__ был введён только в Python 3, Django может сериализовать только следующий шаблон (не связанный метод, используемый в теле класса) на Python 3 и не сможет сериализовать ссылку на него на Python 2:
class MyModel(models.Model):
def upload_to(self):
return "something dynamic"
my_file = models.FileField(upload_to=upload_to)
Если вы используете Python 2, рекомендуется перенести свои методы для upload_to и аналогичные аргументы, принимающие вызываемые объекты (например, default) в тело основного модуля, а не в тело класса.
Добавление метода deconstruct()
Вы можете позволить Django сериализовать собственные экземпляры пользовательских классов, предоставив классу метод deconstruct(). Он не принимает аргументов и должен возвращать кортеж из трёх элементов (path, args, kwargs):
-
pathдолжен быть полным именем класса в Python, включая имя класса (например,myapp.custom_things.MyClass). Если ваш класс недоступен в верхнем уровне модуля, он не может быть сериализован. -
argsдолжен быть списком позиционных аргументов, которые нужно передать методу__init__вашего класса. Все элементы этого списка должны быть сериализуемы. -
kwargsдолжен быть словарем ключевых аргументов, которые нужно передать методу__init__вашего класса. Каждое значение должно быть сериализуемо.
Примечание
Это значение возврата отличается от метода deconstruct() для пользовательских полей, который возвращает кортеж из четырёх элементов.
Django запишет значение как экземпляр вашего класса с указанными аргументами, подобно тому, как он записывает ссылки на поля Django.
Чтобы предотвратить создание новой миграции каждый раз, когда выполняется makemigrations, вы также должны добавить метод __eq__() в декорированный класс. Эта функция будет вызываться механизмом миграции Django для определения изменений между состояниями.
До тех пор, пока все аргументы конструктора вашего класса сами по себе сериализуемы, вы можете использовать декоратор класса @deconstructible из django.utils.deconstruct для добавления метода deconstruct():
from django.utils.deconstruct import deconstructible
@deconstructible
class MyCustomClass(object):
def __init__(self, foo=1):
self.foo = foo
...
def __eq__(self, other):
return self.foo == other.foo
Декоратор добавляет логику для захвата и сохранения аргументов по мере их передачи в конструктор, а затем возвращает эти аргументы точно при вызове deconstruct().
Поддержка Python 2 и 3
Для генерации миграций, поддерживающих как Python 2, так и 3, все строковые литералы, используемые в ваших моделях и полях (например, verbose_name, related_name, и т. д.), должны последовательно представляться байтовыми строками или строками Unicode как в Python 2, так и в Python 3 (а не байтами в Python 2 и строками в Python 3, что является стандартным для немаркированных строковых литералов). В противном случае запуск makemigrations в Python 3 сгенерирует ложные новые миграции для преобразования всех этих строковых атрибутов в строки Unicode.
Самый простой способ добиться этого — следовать рекомендациям из руководства по переносу на Python 3 в Django и убедиться, что все ваши модули начинаются с from __future__ import unicode_literals, чтобы все немаркированные строковые литералы всегда были строками Unicode, независимо от версии Python. При добавлении этого в приложение с существующими миграциями, сгенерированными в Python 2, следующий запуск makemigrations в Python 3, скорее всего, сгенерирует множество изменений, так как он преобразует все атрибуты байтовых строк в строки Unicode; это нормально и должно произойти только один раз.
Поддержка нескольких версий Django
Если вы являетесь разработчиком стороннего приложения с моделями, вам может потребоваться создать миграции, поддерживающие несколько версий Django. В этом случае вы всегда должны запускать makemigrations с самой низкой поддерживаемой версией Django.
Система миграций будет поддерживать обратную совместимость в соответствии с той же политикой, что и остальная часть Django, поэтому файлы миграций, сгенерированные в Django X.Y, должны работать без изменений в Django X.Y+1. Однако система миграций не гарантирует совместимость в сторону увеличения версии. Могут быть добавлены новые функции, и файлы миграций, сгенерированные с более новыми версиями Django, могут не работать в более старых версиях.
Обновление с South
Если у вас уже есть предварительно созданные миграции, созданные с помощью South, то процесс обновления для использования django.db.migrations довольно прост:
- Убедитесь, что все установленные пакеты полностью обновлены с помощью своих миграций.
- Удалите
'south'изINSTALLED_APPS. - Удалите все файлы миграций (с номерами), но не саму папку или
__init__.py- убедитесь, что вы удалили также файлы.pyc. - Запустите
python manage.py makemigrations. Django должен увидеть пустые каталоги миграций и создать новые начальные миграции в новом формате. - Запустите
python manage.py migrate --fake-initial. Django увидит, что таблицы для начальных миграций уже существуют, и отметит их как применённые, не выполняя их. (Django не будет проверять соответствие схемы таблиц вашим моделям, а только то, что таблицы с правильными именами существуют).
Вот и всё! Единственная сложность — если у вас есть циклическая зависимость между внешними ключами; в этом случае makemigrations может создать больше одной начальной миграции, и вам нужно будет отметить их все как применённые с помощью:
python manage.py migrate --fake yourappnamehere
Флаг --fake-initial был добавлен к migrate; ранее начальные миграции всегда автоматически подключались в фейковом режиме, если обнаруживались существующие таблицы.
Библиотеки/Сторонние приложения
Если вы являетесь разработчиком библиотеки или приложения и хотите поддерживать миграции South (для Django 1.6 и ниже) и миграции Django (для 1.7 и выше), вам следует хранить два параллельных набора миграций в вашем приложении, один в каждом формате.
Для этого South 1.0 автоматически сначала будет искать миграции формата South в папке south_migrations , прежде чем искать в migrations, что означает, что проекты пользователей прозрачно будут использовать правильный набор, если вы разместите ваши миграции South в папке south_migrations, а ваши миграции Django в папке migrations.
Дополнительную информацию можно найти в примечаниях к выпуску South 1.0.
См. также
- Справочник по операциям миграций
- Охватывает API операций со схемой, специальные операции и написание собственных операций.
- Руководство по написанию миграций
- Объясняет структуру и написание миграций базы данных для различных сценариев, которые могут возникнуть.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/migrations/