Spec-Zone.ru › Django 2.1

Миграции

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

Команды

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

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

Представьте миграции как систему контроля версий для схемы вашей базы данных. makemigrations отвечает за упаковку изменений модели в отдельные файлы миграций — аналогично коммитам — и migrate отвечает за их применение к вашей базе данных.

Файлы миграций для каждого приложения находятся в каталоге «migrations» внутри этого приложения и предназначены для добавления в базу кода и распространения вместе с ним. Вы должны создавать их один раз на своей машине разработки, а затем запускать те же миграции на машинах коллег, на тестовых машинах и, в конечном итоге, на машинах производства.

Примечание

Можно переопределить имя пакета, содержащего миграции, на уровне приложения, изменив параметр MIGRATION_MODULES.

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

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

Поддержка БД

Миграции поддерживаются всеми встроенными в Django БД, а также любыми сторонними БД, если они реализуют поддержку изменения схемы (через класс SchemaEditor).

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

PostgreSQL

PostgreSQL — самая способная из всех здесь баз данных в плане поддержки схем.

Единственный нюанс заключается в том, что до PostgreSQL 11 добавление столбцов со значениями по умолчанию приводит к полной перезаписи таблицы за время, пропорциональное её размеру. По этой причине рекомендуется всегда создавать новые столбцы со 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 миграции должен включать последнюю миграцию каждого участвующего приложения. В противном случае при попытке извлечь модель в функции RunPython с помощью apps.get_model() может возникнуть ошибка, похожая на LookupError: No installed app with label 'myappname'.

В следующем примере у нас есть миграция в 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, выделите одну из внешних зависимостей в цикле циклической зависимости в отдельной миграции и перенесите зависимость от другого приложения вместе с ней. Если вы не уверены, посмотрите, как 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() и functools.partialmethod с сериализуемыми func, args, и keywords значениями.
  • экземпляры LazyObject которые обертывают сериализуемое значение.
  • Любое поле Django
  • Любая ссылка на функцию или метод (например, datetime.datetime.today) (должна быть в глобальной области видимости модуля)
  • Несвязанные методы, используемые внутри тела класса
  • Любая ссылка на класс (должна быть в глобальной области видимости модуля)
  • Любой элемент с пользовательским методом deconstruct() (см. ниже)
Изменено в Django 2.1:

Добавлена поддержка сериализации для functools.partialmethod.

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.1/topics/migrations/

Spec-Zone.ru

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