• 3.1
  • 3.2
  • 5.0
  • Версия документации: 6.1

Миграции

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

Команды

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

  • migrate, которая отвечает за применение миграций, за откат миграций и за вывод статуса миграций.

  • makemigrations, которая отвечает за создание новых миграций на основе изменений в моделях.

  • sqlmigrate, которая выводит SQL запросы для миграции.

  • sqlmigrate, которая выводит SQL запросы для миграции.

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

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

Примечание

Вы можете изменить пакет, который хранит миграции, указав его в настройке MIGRATION_MODULES.

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

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

Поддержка бэкендами

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

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

PostgreSQL

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

MySQL

В MySQL отсутствует поддержка транзакций при изменении структуры. Это означает, что если миграция не выполнится из-за ошибки вам придется вручную откатывать изменения, чтобы попытаться снова (т.к. невозможно вернуться к исходному состоянию автоматически).

В MySQL 8.0 были внесены значительные улучшения производительности для операций DDL, что сделало их более эффективными и уменьшило необходимость полного перестроения таблицы. Однако он не может гарантировать полного отсутствия блокировок или перебоев. В ситуациях, когда блокировки по-прежнему необходимы, продолжительность этих операций будет пропорциональна количеству задействованных строк.

Наконец, MySQL имеет относительно небольшой предел совокупного размера всех столбцов, охватываемых индексом. Это означает, что индексы, которые возможны на других серверах, не смогут быть созданы в MySQL.

SQLite

SQLite очень плохо поддерживает изменения в структуре базы данных, но Django пытается эмулировать их следующим образом:

  • Создание новой таблицы для новой структуры

  • Копирование данных в новую таблицу

  • Удаление старой таблицы

  • Переименование новой таблицы

Этот процесс как правило хорошо работает, но может быть медленным и иногда глючит. Не рекомендуется использовать и мигрировать SQLite на «боевом» сервере, если вы не очень осведомлены о рисках и его ограничениях. Django поддерживает SQLite, чтобы позволить разработчикам использовать 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:
  Applying books.0003_auto... OK

После того, как миграция отработала, добавьте миграции и изменения к моделям в одном коммите - таким образом, когда другие разработчики (или на «боевом» сервере) обновят код, они получат как изменения ваших моделей, так и миграции для них.

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

$ python manage.py makemigrations --name changed_my_model your_app_label

Контроль версий

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

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

Когда это произойдет, Django предложит выбрать один из вариантов действий. Если Django определит, что достаточно безопасно может сам решить проблему, он предложит автоматически поменять порядок конфликтующим миграциям. Если нет, то вам придется самостоятельно это сделать - не волнуйтесь, это не трудно, и описано в Файлы с миграциями ниже.

Транзакции

В базах данных, поддерживающих транзакции DDL (SQLite и PostgreSQL), все операции миграции по умолчанию выполняются внутри одной транзакции. Напротив, если база данных не поддерживает транзакции DDL (например, MySQL, Oracle), то все операции будут выполняться без транзакции.

Вы можете предотвратить запуск миграции в транзакции, установив для атрибута atomic значение False. Например:

from django.db import migrations


class Migration(migrations.Migration):
    atomic = False

Также возможно выполнить части миграции внутри транзакции, используя atomic() или передав atomic=True в RunPython. Подробнее см. Неатомарные миграции.

Зависимости

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

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

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

Приложения без миграции не должны иметь связей («ForeignKey», «ManyToManyField» и т. д.) с приложениями с миграцией. Иногда это может работать, но не поддерживается.

Сменные зависимости

django.db.migrations.swappable_dependency(value)

Функция swappable_dependent() используется в миграциях для объявления «заменяемых» зависимостей при миграции в приложении замененной модели, в настоящее время при первой миграции этого приложения. Как следствие, замененная модель должна быть создана при первоначальной миграции. Аргумент value представляет собой строку "<метка приложения>.<модель>", описывающую метку приложения и имя модели, например. "myapp.MyModel".

Используя swappable_dependent(), вы сообщаете платформе миграции, что миграция основана на другой миграции, которая устанавливает заменяемую модель, что позволяет в будущем заменить модель другой реализацией. Обычно это используется для ссылки на модели, которые подлежат настройке или замене, например, пользовательскую модель пользователя (settings.AUTH_USER_MODEL, которая по умолчанию равна "auth.User") в системе аутентификации Django.

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

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

При загрузке файла миграции (импортируя как Python модуль) Django ищет дочерний класс django.db.migrations.Migration, который должен называться Migration. Затем он анализирует четыре атрибута класса, из которых обычно используются только два:

  • dependencies - список зависимых миграций.

  • operations - список подклассов Operation, которые определяют необходимые для миграции операции.

Список операций - самое главное в миграции. Он декларативно описывает необходимые операции для изменения структуры базы данных. Django анализирует все операции и в памяти строит схему всех необходимых изменений, чтобы сгенерировать необходимые SQL-запросы.

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

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

Собственные поля

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

Если вы используете функцию :meth:`~django.db.models.from_queryset`для создания менеджера, вам следует унаследоваться от сгенерированного класса:

class MyManager(MyBaseManager.from_queryset(CustomQuerySet)):
    use_in_migrations = True


class MyModel(models.Model):
    objects = MyManager()

Советуем ознакомиться с заметками в разделе «Исторические» модели.

Начальные миграции

Migration.initial

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

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

При запуске migrate с опцией :djadminopt:`--fake-initial` начальные миграция выполняются по особенному. Если создаются таблицы (операция CreateModel), Django проверит наличие таблиц в базе данных, и пометит миграцию выполненной, если таблицы найдены. Аналогично при добавлении полей (операция AddField), Django проверит наличие полей. Без опции :djadminopt:`--fake-initial` начальные миграции выполняются как и обычные.

Последовательность истории

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

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

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

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

$ python manage.py makemigrations your_app_label

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

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

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

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

Реверсивные миграции

Миграцию можно отменить с помощью migrate, передав номер предыдущей миграции. Например, чтобы отменить миграцию books.0003:

$ python manage.py migrate books 0002
Operations to perform:
  Target specific migration: 0002_auto, from books
Running migrations:
  Rendering model states... DONE
  Unapplying books.0003_auto... OK
...\> py manage.py migrate books 0002
Operations to perform:
  Target specific migration: 0002_auto, from books
Running migrations:
  Rendering model states... DONE
  Unapplying books.0003_auto... OK

Если вы хотите отменить все миграции, примененные к приложению, используйте имя «ноль»:

$ python manage.py migrate books zero
Operations to perform:
  Unapply all migrations: books
Running migrations:
  Rendering model states... DONE
  Unapplying books.0002_auto... OK
  Unapplying books.0001_initial... OK
...\> py manage.py migrate books zero
Operations to perform:
  Unapply all migrations: books
Running migrations:
  Rendering model states... DONE
  Unapplying books.0002_auto... OK
  Unapplying books.0001_initial... OK

Миграция является необратимой, если она содержит какие-либо необратимые операции. Попытка отменить такую ​​миграцию вызовет ошибку «IrreversibleError»:

$ python manage.py migrate books 0002
Operations to perform:
  Target specific migration: 0002_auto, from books
Running migrations:
  Rendering model states... DONE
  Unapplying books.0003_auto...Traceback (most recent call last):
django.db.migrations.exceptions.IrreversibleError: Operation <RunSQL  sql='DROP TABLE demo_books'> in books.0003_auto is not reversible
...\> py manage.py migrate books 0002
Operations to perform:
  Target specific migration: 0002_auto, from books
Running migrations:
  Rendering model states... DONE
  Unapplying books.0003_auto...Traceback (most recent call last):
django.db.migrations.exceptions.IrreversibleError: Operation <RunSQL  sql='DROP TABLE demo_books'> in books.0003_auto is not reversible

«Исторические» модели

При выполнении миграций Django использует сгенерированные версии ваших моделей, которые хранятся в файлах миграций. Если вы пишете Python код для выполнения миграции, используя операцию RunPython, или добавляете метод allow_migrate в ваш роутер базы данных, Django предоставит вам эти модели для использования.

Предупреждение

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

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

Т.к. невозможно сериализовать произвольный код Python, эти версии моделей не будут содержать методы, который вы добавили в модели. Однако, модели будут содержать аналогичные поля, связи, менеджеры(только те, которые содержат use_in_migrations = True) и параметры Meta (с учетом версии модели, так что они могут отличаться от моделей, которые находятся на данный момент в приложении).

Предупреждение

Это означает, что вы НЕ сможете использовать переопределённый метод save(). Также НЕ будет использоваться переопределённый конструктор модели. Вы должны учитывать это при создании Python кода миграций!

Ссылки на функции, которые используются в параметрах поля (например, upload_to и limit_choices_to), и менеджеры моделей с use_in_migrations = True будут сериализированы в миграциях. Такие функции нельзя удалять из кода проекта, пока существуют миграции, которые ссылаются на них. Все собственные поля моделей также должы быть доступны т.к. они явно импортируются в миграциях.

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

Чтобы удалить старые ссылки, вы можете сжать миграции или, если ссылок не так много, скопировать их в файлы миграции.

Советы по удалению полей модели

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

Чтобы помочь решить эту проблему, были добавлены атрибуты в поля модели, которые могут указать на устаревшие поля при проверке проекта.

Добавьте атрибут 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 = f"{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),
    ]

Миграции для продвинутых

Если вы хотите узнать как писать более сложные миграции, или как создавать миграции с нуля, смотрите раздел об операциях в миграциях и «how-to» о создании миграций.

Объединение миграций

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

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

Django загрузит все ваши миграции, соберёт последовательность объектов Operation, затем попытается оптимизировать и сократить этот список. Например, Django понимает, что CreateModel и DeleteModel отменяют друг друга, и что AddField может быть добавлена в CreateModel.

Когда список операций будет оптимизирован насколько возможно - это зависит от сложности связей между моделями, использовались ли операции RunSQL или RunPython (которые нельзя оптимизировать) - 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? [y/N] y
Optimizing...
  Optimized from 12 operations to 7 operations.
Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_something.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, если вы хотите установить имя сжатой миграции, а не использовать автоматически сгенерированное.

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

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

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

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

  • Удалите все миграции, которые она заменяет

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

  • Удалите аргумент replaces в классе Migration объединенной миграции (он указывает Django, что это объединенная миграция)

Примечание

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

Удаление ссылок на удаленные миграции

Если есть вероятность, что вы сможете повторно использовать имя удаленной миграции в будущем, вам следует удалить ссылки на нее из таблицы миграций Django с помощью опции migrate --prune.

Changed in Django 6.0:

Была добавлена ​​поддержка подавления сжатых миграций.

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

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

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

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

  • int, long, float, bool, str, unicode, bytes, None

  • список, набор, кортеж, дикт, диапазон

  • Объекты datetime.date, datetime.time и datetime.datetime (включая те, у которых указан часовой пояс)

  • Экземпляры zoneinfo.ZoneInfo

  • Объекты decimal.Decimal

  • Экземпляры enum.Enum и enum.Flag

  • Объекты decimal.Decimal

  • Экземпляры functools.partial() и functools.partialmethod, которые имеют сериализуемые значения func, args и keywords.

  • Чистые и конкретные объекты пути из pathlib. Конкретные пути преобразуются в их чистый эквивалент пути, например. от pathlib.PosixPath до pathlib.PurePosixPath

  • Экземпляры os.PathLike, например. os.DirEntry, которые преобразуются в str или bytes с помощью os.fspath()

  • Экземпляры LazyObject, которые оборачивают сериализуемое значение.

  • Экземпляры типов перечисления (например, TextChoices или IntegerChoices).

  • Любое поле Django

  • Ссылку на любую функцию или метод (например datetime.datetime.today) (должны быть доступны на уровне модуля)

    • Функции могут быть декорированы, если они правильно обернуты, например, с использованием functools.wraps()

    • Декораторы functools.cache() и functools.lru_cache() явно поддерживаются.

  • Непривязанные методы в теле класса (смотрите ниже)

  • Ссылка на класс (должны быть доступны на уровне модуля)

  • Любой объект с методом deconstruct() (смотрите ниже)

Changed in Django 6.0:

Добавлена ​​поддержка сериализации для экземпляров zoneinfo.ZoneInfo.

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

  • Вложенные классы

  • Экземпляры произвольного класса (например MyClass(4.3, 5.7))

  • Лямбда-функции

Собственные поля

Вы можете сериализовать другие типы, написав собственный сериализатор. Например, если Django не сериализовал Decimal по умолчанию, вы могли бы сделать это:

from decimal import Decimal

from django.db.migrations.serializer import BaseSerializer
from django.db.migrations.writer import MigrationWriter


class DecimalSerializer(BaseSerializer):
    def serialize(self):
        return repr(self.value), {"from decimal import Decimal"}


MigrationWriter.register_serializer(Decimal, DecimalSerializer)

Первый аргумент MigrationWriter.register_serializer() — это тип или итерация типов, которые должны использовать сериализатор.

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

Метод 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 операций для изменений структуры базы данных, специальные операции, и создания своих операций.

«how-to» о создании миграций

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

Back to Top