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

Миграции

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

Команды

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

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

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

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

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

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

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

Примечание

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

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

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

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

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

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

PostgreSQL

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

The only caveat is that prior to PostgreSQL 11, adding columns with default values causes a full rewrite of the table, for a time proportional to its size. For this reason, it’s recommended you always create new columns with null=True, as this way they will be added immediately.

MySQL

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

In addition, MySQL will fully rewrite tables for almost every schema operation and generally takes a time proportional to the number of rows in the table to add or remove columns. On slower hardware this can be worse than a minute per million rows - adding a few columns to a table with just a few million rows could lock your site up for over ten minutes.

Finally, MySQL has relatively small limits on name lengths for columns, tables and indexes, as well as a limit on the combined size of all columns an index covers. This means that indexes that are possible on other backends will fail to be created under MySQL.

SQLite

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

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

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

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

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

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

Работа с миграциями

Django can create migrations for you. Make changes to your models - say, add a field and remove a model - and then run makemigrations:

$ python manage.py makemigrations
Migrations for 'books':
  books/migrations/0003_auto.py:
    - Alter field author on book

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

Once you have your new migration files, you should apply them to your database to make sure they work as expected:

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

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

If you want to give the migration(s) a meaningful name instead of a generated one, you can use the makemigrations --name option:

$ 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.

This means that when you run the migrations, the authors migration runs first and creates the table the ForeignKey references, and then the migration that makes the ForeignKey column runs afterwards and creates the constraint. If this didn’t happen, the migration would try to create the ForeignKey column without the table it’s referencing existing and your database would throw an error.

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

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

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

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

If your app already has models and database tables, and doesn’t have migrations yet (for example, you created it against a previous Django version), you’ll need to convert it to use migrations by running:

$ 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 предоставит вам эти модели для использования.

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

If you import models directly rather than using the historical models, your migrations may work initially but will fail in the future when you try to re-run old migrations (commonly, when you set up a new installation and run through all the migrations to set up the database).

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

Т.к. невозможно сериализовать произвольный код 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.

To start, make an empty migration file you can work from (Django will put the file in the right place, suggest a name, and add dependencies for you):

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),
    ]

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

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

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

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

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

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

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

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

Это позволяет объединить миграции и не сломать проект на сервере, который ещё не обновили до последней версии. Рекомендуем следующую последовательность действий: объединяем миграции, старые не удаляем, коммитим и обновляем все сервера при следующем релизе (если это распространяемое приложение, убеждаемся, что все пользователи обновились до последней версии), удаляем старые миграции, коммитим и обновляем сервера до последней релизной версии.

The command that backs all this is squashmigrations - pass it the app label and migration name you want to squash up to, and it’ll get to work:

$ ./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, если вы хотите установить имя сжатой миграции, а не использовать автоматически сгенерированное.

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

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

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

После этого объединенную миграцию можно преобразовать в обычную начальную миграцию:

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

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

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

Примечание

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

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

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

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

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

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

  • list, set, tuple, dict

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

  • Объекты decimal.Decimal

  • enum.Enum instances

  • Объекты decimal.Decimal

  • Объекты functools.partial, которые содержат сериализируемые значения func, args и keywords.

  • Объекты LazyObject, которые содержат сериализируемое значение.

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

  • Любое поле Django

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

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

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

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

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