Миграции

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

Команды

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

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

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

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

Заметка

Для каждого приложения можно переопределить имя пакета, который содержит миграции, используя параметр MIGRATION_MODULES .

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

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

Поддерживаемые базы данных

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

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

PostgreSQL

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

Единственное ограничение заключается в том, что до PostgreSQL 11 добавление столбцов со значениями по умолчанию приводит к полной перезаписи таблицы за время, пропорциональное ее размеру. По этой причине рекомендуется всегда создавать с его помощью новые столбцы null=True , потому что таким образом они будут добавляться немедленно.

MySQL

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

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

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

SQLite

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

  • Создание новой таблицы для новой схемы
  • Копирование данных из одного в другой
  • Удаление старой таблицы
  • Переименование новой таблицы с именем старой таблицы

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

Процедуры

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

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

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

Приложения без миграции не должны иметь отношений (ForeignKey` ManyToManyField и т. Д.) С приложениями с миграциями. Иногда это может сработать, но 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)),
    ]

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

  • dependencies , список миграций, от которых это зависит.
  • operations , список классов, Operation определяющих, что делает эта миграция.

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

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

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

Настраиваемые поля

Невозможно изменить количество позиционных параметров в уже перенесенном настраиваемом поле без создания исключения TypeError . Старая миграция вызовет __init__ измененный метод со старой подписью. Поэтому, если вам нужен новый параметр, создайте вместо него именованный параметр и добавьте что-то подобное в конструктор.assert 'nom_du_paramètre' 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

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

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

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

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

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

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

$ python manage.py makemigrations your_app_label

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

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

  • С момента создания соответствующих таблиц модели не изменялись. Чтобы миграции работали, вы должны сначала создать начальную миграцию, а затем внести изменения, потому что 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

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

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

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

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

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

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

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

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

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

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

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

Рекомендации при удалении полей из моделей

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

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

Добавьте атрибут system_check_deprecated_details в поле затронутой модели следующим образом:

class IPAddressField(Field):
    system_check_deprecated_details = {
        'msg': (
            'IPAddressField has been deprecated. Support for it (except '
            'in historical migrations) will be removed in Django 1.9.'
        ),
        'hint': 'Use GenericIPAddressField instead.',  # optional
        'id': 'fields.W900',  # pick a unique ID for your field.
    }

После периода устаревания по вашему выбору (две или три основные версии для полей, специфичных для Django), измените атрибут system_check_deprecated_details на system_check_removed_details и обновите словарь согласно следующему шаблону:

class IPAddressField(Field):
    system_check_removed_details = {
        'msg': (
            'IPAddressField has been removed except for support in '
            'historical migrations.'
        ),
        'hint': 'Use GenericIPAddressField instead.',
        'id': 'fields.E900',  # pick a unique ID for your field.
    }

Всегда сохраняйте методы полей, которые необходимы для работы в контексте миграции базы данных, например __init__() , deconstruct() и get_internal_type() . Сохраняйте это историческое поле до тех пор, пока все еще существуют связанные с ним миграции. Например, после объединения миграций и удаления старых можно полностью удалить это старое поле.

Миграция данных

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

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

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

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

python manage.py makemigrations --empty yourappname

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

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

class Migration(migrations.Migration):

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

    operations = [
    ]

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

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

from django.db import migrations

def combine_names(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Person = apps.get_model('yourappname', 'Person')
    for person in Person.objects.all():
        person.name = '%s %s' % (person.first_name, person.last_name)
        person.save()

class Migration(migrations.Migration):

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

    operations = [
        migrations.RunPython(combine_names),
    ]

Как только это будет сделано, мы сможем нормально запускать, и перенос данных будет выполняться, как и любой другой перенос.python manage.py migrate

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

Доступ к шаблонам из других приложений

Когда вы пишете функцию, вызываемую пользователем, RunPython которая использует шаблоны из приложений, отличных от того, в котором расположена dependencies миграция, атрибут миграции должен включать последнюю миграцию каждого приложения, которое вызывает проблему, иначе вы можете получить ошибка, похожая на: когда вы пытаетесь получить модель в функции, вызываемой .LookupError: Aucune application installée pour 'myappname' apps.get_model() RunPython

В следующем примере у нас есть миграция, в 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 делает это, беря все существующие миграции, извлекая их операции, помещая их все в строку, а затем запускает оптимизатор, чтобы попытаться сохранить этот список операций как можно меньшим. Например, он знает это CreateModel и DeleteModel компенсирует друг друга, и это AddField можно интегрировать CreateModel .

Как только цепочка операций минимизирована, Django записывает результат в новый набор файлов миграции. Это минимальное количество зависит от количества относительных зависимостей между моделями и наличия операций 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 ,NoneType
  • list , set , tuple , dict , range .
  • Случаи datetime.date , datetime.time и datetime.datetime ( в том числе и те , которые содержат часовой пояс)
  • Инстансы decimal.Decimal
  • Инстансы enum.Enum
  • Инстансы uuid.UUID
  • экземпляры functools.partial() и из functools.partialmethod которых имеют сериализуемые значения func , args и keywords .
  • экземпляры, LazyObject которые украшают сериализуемое значение.
  • Экземпляры перечислительных типов (например, TextChoices или IntegerChoices ).
  • Все элементы управления Django
  • Ссылка на любую функцию или метод (например, datetime.datetime.today ) (должна быть определена на уровне основного модуля)
  • Несвязанные методы, используемые внутри тела класса
  • Ссылка на любой класс (должна быть определена на уровне основного модуля)
  • Любой объект с методом deconstruct() (см. Ниже )

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

  • Вложенные классы
  • Экземпляры произвольных классов (например )MaClasse(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 к классу с последним включенным именем класса (например, monapp.quelque_chose.MaClasse ). Если класс не определен на первом уровне модуля, его нельзя сериализовать.
  • 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. В этом случае вы всегда должны работать с самой низкой версией Django, которую вы хотите поддерживать .makemigrations

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

Смотрите также

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

Copyright ©2021 All rights reserved