Spec-Zone.ru › Django 2.0

Миграции

Миграции — это способ Django распространять изменения, которые вы вносите в свои модели (добавление поля, удаление модели и т. д.), в схему вашей базы данных. Они разработаны, чтобы быть в основном автоматическими, но вам нужно знать, когда создавать миграции, когда их запускать и какие распространенные проблемы могут возникнуть.

Команды

Существует несколько команд, которые вы будете использовать для взаимодействия с миграциями и обработкой схемы базы данных Django:

  • migrate, которая отвечает за применение и отмену миграций.
  • makemigrations, которая отвечает за создание новых миграций на основе изменений, которые вы внесли в свои модели.
  • sqlmigrate, которая отображает SQL-запросы для миграции.
  • showmigrations, которая отображает миграции проекта и их состояние.

Представьте миграции как систему управления версиями для схемы вашей базы данных. 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':
  books/migrations/0003_auto.py:
    - Alter field author on book

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

После получения новых файлов миграции вы должны применить их к своей базе данных, чтобы убедиться, что они работают как ожидается:

$ python manage.py migrate
Operations to perform:
  Apply all migrations: books
Running migrations:
  Rendering model states... DONE
  Applying books.0003_auto... OK

После применения миграции, сохраните миграцию и изменение модели в вашей системе управления версиями как один коммит — таким образом, когда другие разработчики (или ваши серверы производства) проведут обновление кода, они получат и изменения в ваших моделях, и соответствующую миграцию одновременно.

Если вы хотите присвоить миграции(ям) осмысленное имя вместо сгенерированного, вы можете использовать опцию makemigrations --name:

$ python manage.py makemigrations --name changed_my_model your_app_label

Система управления версиями

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

Не беспокойтесь — номера предназначены только для справки разработчиков, Django интересует только то, что каждая миграция имеет другое имя. Миграции указывают, от каких других миграций они зависят — включая более ранние миграции в том же приложении — в файле, поэтому можно определить, когда для одного приложения есть две новые миграции, которые не упорядочены.

В этом случае Django запросит у вас и предложит несколько вариантов. Если посчитает это достаточно безопасным, он предложит автоматически упорядочить две миграции. Если нет, вам нужно будет внести изменения в миграции самостоятельно — не беспокойтесь, это не сложно и подробно описано в Файлы миграций ниже.

Зависимости

Хотя миграции относятся к каждому приложению, таблицы и отношения, подразумеваемые вашими моделями, слишком сложны, чтобы создавать их только для одного приложения за раз. Когда вы создаете миграцию, которая требует выполнения чего-то еще — например, добавляете ForeignKey в ваше books приложение в приложение authors — полученная миграция будет содержать зависимость от миграции в authors.

Это означает, что при запуске миграций миграция authors выполняется первой и создает таблицу, на которую ссылается ForeignKey, а затем миграция, которая создает столбец ForeignKey, выполняется после и создает ограничение. Если бы этого не было, миграция попыталась бы создать столбец ForeignKey без существования ссылающейся на него таблицы, и ваша база данных вывела бы ошибку.

Это поведение зависимости влияет на большинство операций миграции, где вы ограничиваетесь одним приложением. Ограничение на одно приложение (либо в makemigrations или migrate) — это обещание лучшего результата, а не гарантия; будут использоваться любые другие приложения, необходимые для корректного определения зависимостей.

Файлы миграций

Миграции хранятся в формате на диске, который здесь называется «файлами миграций». Эти файлы фактически являются обычными 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()

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

Первоначальные миграции

Migration.initial

«Первоначальные миграции» для приложения — это миграции, которые создают первую версию таблиц этого приложения. Обычно приложение имеет только одну первоначальную миграцию, но в некоторых случаях сложной взаимозависимости моделей она может иметь две или более.

Первоначальные миграции отмечаются атрибутом initial = True класса миграции. Если атрибут initial не найден, миграция будет считаться «первоначальной», если это первая миграция в приложении (т. е. если она не зависит от какой-либо другой миграции в том же приложении).

Когда используется опция migrate --fake-initial, эти первоначальные миграции обрабатываются особым образом. Для первоначальной миграции, которая создаёт одну или несколько таблиц (CreateModel операция), Django проверяет, существуют ли все эти таблицы в базе данных, и имитирует применение миграции, если это так. Аналогично, для первоначальной миграции, которая добавляет одно или несколько полей (AddField операция), Django проверяет, существуют ли все соответствующие столбцы в базе данных, и имитирует применение миграции, если это так. Без --fake-initial, первоначальные миграции обрабатываются неотличимо от любой другой миграции.

Согласованность истории

Как уже обсуждалось, вам может потребоваться вручную линейно упорядочить миграции при объединении двух ветвей разработки. При редактировании зависимостей миграций вы можете непреднамеренно создать несогласованное состояние истории, в котором миграция была применена, но некоторые из её зависимостей нет. Это чёткое указание на то, что зависимости указаны неверно, поэтому Django откажется от выполнения миграций или создания новых миграций, пока это не будет исправлено. При использовании нескольких баз данных вы можете использовать метод allow_migrate() роутеров баз данных, чтобы контролировать, в каких базах данных makemigrations проверяется согласованность истории.

Добавление миграций в приложения

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

Если в вашем приложении уже есть модели и таблицы базы данных, но ещё нет миграций (например, вы создали его с предыдущей версией Django), вам нужно преобразовать его для использования миграций; это простая процедура:

$ python manage.py makemigrations your_app_label

Это создаст новую первоначальную миграцию для вашего приложения. Теперь выполните python manage.py migrate --fake-initial, и Django обнаружит, что у вас есть первоначальная миграция и что таблицы, которые он хочет создать, уже существуют, и пометит миграцию как уже применённую. (Без флага migrate --fake-initial команда выдаст ошибку, потому что таблицы, которые он хочет создать, уже существуют.)

Обратите внимание, что это работает только при соблюдении двух условий:

  • Вы не изменяли модели с момента создания их таблиц. Для работы миграций вы должны сначала создать первоначальную миграцию, а затем внести изменения, так как Django сравнивает изменения с файлами миграций, а не с базой данных.
  • Вы не редактировали базу данных вручную — 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

Затем откройте файл; он должен выглядеть примерно так:

# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
    ]

Теперь всё, что вам нужно сделать, это создать новую функцию и заставить RunPython её использовать. RunPython ожидает в качестве аргумента вызываемый объект, который принимает два аргумента — первый — регистр приложений, который загружает исторические версии всех ваших моделей, чтобы соответствовать тому, где в вашей истории находится миграция, а второй — SchemaEditor, который вы можете использовать для ручного изменения схемы базы данных (но будьте осторожны, это может сбить с толку автодетектора миграций!)

Давайте напишем простую миграцию, которая заполняет наше новое поле name комбинированными значениями first_name и last_name (мы пришли к выводу, что у всех есть имя и фамилия). Всё, что нам нужно сделать, это использовать историческую модель и перебирать строки:

from django.db import 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 (которые нельзя оптимизировать, если они не помечены как elidable) — 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.

Используйте опцию squashmigrations --squashed-name, если хотите задать имя сжатой миграции вместо автоматически сгенерированного.

Обратите внимание, что взаимозависимости моделей в Django могут стать очень сложными, и сжатие может привести к миграциям, которые не выполняются; либо неправильно оптимизированы (в этом случае вы можете попробовать снова с --no-optimize, хотя вам также следует сообщить об ошибке), либо с CircularDependencyError, в этом случае вы можете вручную её решить.

Для ручного разрешения CircularDependencyError, выделите одну из зависимостей ForeignKey в цикле циклической зависимости в отдельную миграцию и перенесите зависимость от другого приложения вместе с ней. Если вы не уверены, посмотрите, как makemigrations справляется с проблемой, когда ей предлагается создать совершенно новые миграции из ваших моделей. В будущей версии Django squashmigrations будет обновлён, чтобы попытаться разрешить эти ошибки самостоятельно.

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

Затем вы должны перевести сжатую миграцию в обычную миграцию:

  • Удалить все файлы миграций, которые она заменяет.
  • Обновить все миграции, которые зависят от удалённых миграций, так чтобы они зависели от сжатой миграции.
  • Удалить атрибут replaces в классе Migration сжатой миграции (так Django понимает, что это сжатая миграция).

Примечание

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

Сериализация значений

Миграции — это просто файлы Python, содержащие старые определения ваших моделей; таким образом, для их записи Django должен принять текущее состояние ваших моделей и сериализовать их в файл.

Хотя Django может сериализовать большинство вещей, существуют некоторые вещи, которые нельзя сериализовать в допустимое представление Python — нет стандартного Python для того, как значение может быть преобразовано обратно в код (repr() работает только для базовых значений и не указывает пути импорта).

Django может сериализовать следующие:

  • int, float, bool, str, bytes, None
  • list, set, tuple, dict
  • datetime.date, datetime.time, и datetime.datetime экземпляры (включая те, которые учитывают часовой пояс)
  • decimal.Decimal экземпляры
  • enum.Enum экземпляры
  • uuid.UUID экземпляры
  • functools.partial экземпляры, у которых есть сериализуемые func, args, и keywords значения.
  • LazyObject экземпляры, которые оборачивают сериализуемое значение.
  • Любое поле Django
  • Любая ссылка на функцию или метод (например, datetime.datetime.today) (должна быть в верхнем уровне модуля)
  • Несвязанные методы, используемые в теле класса
  • Любая ссылка на класс (должна быть в верхнем уровне модуля)
  • Всё, что имеет пользовательский метод deconstruct() (см. ниже)
Изменено в Django 1.11:

Была добавлена поддержка сериализации для uuid.UUID.

Django не может сериализовать:

  • Вложенные классы
  • Произвольные экземпляры классов (например, MyClass(4.3, 5.7))
  • Лямбда-функции

Добавление метода 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:

    def __init__(self, foo=1):
        self.foo = foo
        ...

    def __eq__(self, other):
        return self.foo == other.foo

Декоратор добавляет логику для захвата и сохранения аргументов по мере их попадания в ваш конструктор, а затем возвращает эти аргументы точно так же, когда вызывается deconstruct().

Поддержка нескольких версий Django

Если вы являетесь разработчиком приложения стороннего разработчика с моделями, вам может потребоваться отправить миграции, которые поддерживают несколько версий Django. В этом случае всегда запускайте makemigrations с самой низкой версией Django, которую вы хотите поддержать.

Система миграций будет поддерживать обратную совместимость в соответствии с той же политикой, что и остальная часть Django, поэтому файлы миграций, сгенерированные в Django X.Y, должны работать без изменений в Django X.Y+1. Однако система миграций не гарантирует совместимости в будущее. Возможно добавление новых функций, а файлы миграций, сгенерированные с более новыми версиями Django, могут не работать в более старых версиях.

См. также

Справочник по операциям миграций
Охватывает API операций схемы, специальные операции и создание собственных операций.
Руководство по созданию миграций
Объясняет, как структурировать и создавать миграции базы данных для различных сценариев, с которыми вы можете столкнуться.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/topics/migrations/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API