Spec-Zone.ru › Django 6.0

Операции миграций

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

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

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

Если вам нужен пустой файл миграции, в который можно добавить собственные объекты Operation, используйте python manage.py makemigrations --empty yourappname. Учтите, что добавление вручную операций, изменяющих схему, может сбить с толку автоматический определитель миграций и привести к тому, что последующие запуски makemigrations создадут некорректный код.

Все основные операции Django доступны из модуля django.db.migrations.operations.

Вводные материалы см. в руководстве по миграциям.

Операции со схемой

CreateModel

class CreateModel(name, fields, options=None, bases=None, managers=None) [источник]

Создаёт новую модель в истории проекта и соответствующую ей таблицу в базе данных.

name — это имя модели в том виде, в котором оно было бы указано в файле models.py.

fields — это список из 2-кортежей (field_name, field_instance). Экземпляр поля должен быть непривязанным (то есть просто models.CharField(...), а не поле, взятое из другой модели).

options — необязательный словарь значений из класса модели Meta.

bases — необязательный список других классов, от которых наследуется эта модель; он может содержать как объекты классов, так и строки в формате "appname.ModelName", если требуется зависимость от другой модели (то есть наследование от её исторической версии). Если параметр не указан, по умолчанию используется наследование от стандартного models.Model.

managers принимает список из 2-кортежей (manager_name, manager_instance). Первый менеджер в списке будет менеджером модели по умолчанию во время миграций.

DeleteModel

class DeleteModel(name) [источник]

Удаляет модель из истории проекта, а её таблицу — из базы данных.

RenameModel

class RenameModel(old_name, new_name) [источник]

Переименовывает модель со старого имени на новое.

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

AlterModelTable

class AlterModelTable(name, table) [источник]

Изменяет имя таблицы модели (параметр db_table подкласса Meta).

AlterModelTableComment

class AlterModelTableComment(name, table_comment) [источник]

Изменяет комментарий к таблице модели (параметр db_table_comment подкласса Meta).

AlterUniqueTogether

class AlterUniqueTogether(name, unique_together) [источник]

Изменяет набор ограничений уникальности модели (параметр unique_together подкласса Meta).

AlterIndexTogether

class AlterIndexTogether(name, index_together) [источник]

Изменяет набор пользовательских индексов модели (параметр index_together подкласса Meta).

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

AlterIndexTogether официально поддерживается только в файлах миграций, созданных до Django 4.2. Из соображений обратной совместимости этот параметр по-прежнему входит в публичный API, и планов объявлять его устаревшим или удалять нет, однако в новых миграциях его использовать не следует. Вместо этого используйте операции AddIndex и RemoveIndex.

AlterOrderWithRespectTo

class AlterOrderWithRespectTo(name, order_with_respect_to) [источник]

Создаёт или удаляет столбец _order, необходимый для параметра order_with_respect_to подкласса Meta.

AlterModelOptions

class AlterModelOptions(name, options) [источник]

Сохраняет изменения различных параметров модели (настроек в Meta модели), например permissions и verbose_name. На базу данных не влияет, но сохраняет эти изменения для использования экземплярами RunPython. options должен быть словарём, сопоставляющим имена параметров и значения.

AlterModelManagers

class AlterModelManagers(name, managers) [источник]

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

AddField

class AddField(model_name, name, field, preserve_default=True) [источник]

Добавляет поле в модель. model_name — имя модели, name — имя поля, а field — экземпляр непривязанного Field (объект, который вы указали бы в объявлении поля в models.py, например models.IntegerField(null=True).

Аргумент preserve_default указывает, является ли значение поля по умолчанию постоянным и должно ли оно быть включено в состояние проекта (True), или же оно временное и используется только для этой миграции (False). Обычно это необходимо, когда миграция добавляет в таблицу поле, допускающее значение NULL, и требуется значение по умолчанию для существующих строк. Это не влияет на непосредственную установку значений по умолчанию в базе данных: Django никогда не задаёт значения по умолчанию на уровне базы данных и всегда применяет их в коде Django ORM.

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

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

  • Добавьте поле, допускающее значение NULL, без значения по умолчанию и выполните команду makemigrations. В результате должна быть создана миграция с операцией AddField.
  • Добавьте значение по умолчанию для поля и выполните команду makemigrations. В результате должна быть создана миграция с операцией AlterField.

RemoveField

class RemoveField(model_name, name) [источник]

Удаляет поле из модели.

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

Изменено в Django 6.0:

Бэкенды BaseDatabaseSchemaEditor и PostgreSQL больше не используют CASCADE для удаления зависимых связанных объектов базы данных, например представлений. Возможно, перед выполнением RemoveField потребуется вручную удалить все зависимые объекты, которыми Django не управляет.

AlterField

class AlterField(model_name, name, field, preserve_default=True) [источник]

Изменяет определение поля, в том числе его тип, null, unique, db_column и другие атрибуты поля.

Аргумент preserve_default указывает, является ли значение поля по умолчанию постоянным и должно ли оно быть включено в состояние проекта (True), или же оно временное и используется только для этой миграции (False). Обычно это необходимо, когда миграция изменяет поле, допускающее значение NULL, на поле, которое его не допускает, и требуется значение по умолчанию для существующих строк. Это не влияет на непосредственную установку значений по умолчанию в базе данных: Django никогда не задаёт значения по умолчанию на уровне базы данных и всегда применяет их в коде Django ORM.

Обратите внимание, что не все изменения возможны во всех базах данных. Например, в большинстве баз данных нельзя преобразовать поле текстового типа, такое как models.TextField(), в поле числового типа, такое как models.IntegerField().

RenameField

class RenameField(model_name, old_name, new_name) [источник]

Изменяет имя поля (и, если не задан параметр db_column, имя его столбца).

AddIndex

class AddIndex(model_name, index) [источник]

Создаёт индекс в таблице базы данных для модели с model_name. index — экземпляр класса Index.

RemoveIndex

class RemoveIndex(model_name, name) [источник]

Удаляет индекс с именем name у модели с model_name.

RenameIndex

class RenameIndex(model_name, new_name, old_name=None, old_fields=None) [источник]

Переименовывает индекс в таблице базы данных для модели с model_name. Можно указать ровно один из параметров: old_name или old_fields. old_fields — это итерируемый объект со строками, часто соответствующими полям index_together (параметр, использовавшийся до Django 5.1).

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

AddConstraint

class AddConstraint(model_name, constraint) [источник]

Создаёт ограничение в таблице базы данных для модели с model_name.

RemoveConstraint

class RemoveConstraint(model_name, name) [источник]

Удаляет ограничение с именем name у модели с model_name.

AlterConstraint

Добавлено в Django 5.2.
class AlterConstraint(model_name, name, constraint) [источник]

Изменяет ограничение с именем name модели с model_name, задавая новое значение constraint без изменения базы данных.

Специальные операции

RunSQL

class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False) [источник]

Позволяет выполнять в базе данных произвольный SQL-код. Это полезно для работы с расширенными возможностями бэкендов баз данных, которые Django не поддерживает напрямую.

Если указаны, sql и reverse_sql должны быть строками SQL-запросов для выполнения в базе данных. В большинстве бэкендов баз данных (во всех, кроме PostgreSQL) Django перед выполнением разделяет SQL-код на отдельные инструкции.

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

В PostgreSQL и SQLite используйте в SQL только BEGIN или COMMIT в неатомарных миграциях, чтобы не нарушить состояние транзакций Django.

Также можно передать список строк или 2-кортежей. Последний вариант используется для передачи запросов и параметров так же, как в cursor.execute(). Следующие три операции эквивалентны:

migrations.RunSQL("INSERT INTO musician (name) VALUES ('Reinhardt');")
migrations.RunSQL([("INSERT INTO musician (name) VALUES ('Reinhardt');", None)])
migrations.RunSQL([("INSERT INTO musician (name) VALUES (%s);", ["Reinhardt"])])

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

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

migrations.RunSQL(
    sql=[("INSERT INTO musician (name) VALUES (%s);", ["Reinhardt"])],
    reverse_sql=[("DELETE FROM musician where name=%s;", ["Reinhardt"])],
)

Если reverse_sql имеет значение None (по умолчанию), операцию RunSQL нельзя отменить.

Аргумент state_operations позволяет указать операции, эквивалентные SQL-коду с точки зрения состояния проекта. Например, если вы вручную создаёте столбец, здесь следует передать список, содержащий операцию AddField, чтобы автоматический определитель располагал актуальным состоянием модели. Если этого не сделать, при следующем запуске makemigrations он не обнаружит операцию добавления этого поля и попытается выполнить её снова. Например:

migrations.RunSQL(
    "ALTER TABLE musician ADD COLUMN name varchar(255) NOT NULL;",
    state_operations=[
        migrations.AddField(
            "musician",
            "name",
            models.CharField(max_length=255),
        ),
    ],
)

Необязательный аргумент hints будет передан как **hints методу allow_migrate() маршрутизаторов баз данных, чтобы помочь им выбрать маршрут. Подробнее о подсказках для баз данных см. в разделе Подсказки.

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

RunSQL.noop

Передайте атрибут RunSQL.noop в sql или reverse_sql, если операция не должна выполнять никаких действий в указанном направлении. Это особенно полезно для создания обратимой операции.

RunPython

class RunPython(code, reverse_code=None, atomic=None, hints=None, elidable=False) [источник]

Выполняет пользовательский код Python в историческом контексте. code (и reverse_code, если указан) должны быть вызываемыми объектами, принимающими два аргумента. Первый — экземпляр django.apps.registry.Apps, содержащий исторические модели, соответствующие текущему месту операции в истории проекта; второй — экземпляр SchemaEditor.

Аргумент reverse_code вызывается при отмене миграций. Этот вызываемый объект должен отменять действия, выполненные вызываемым объектом code, чтобы миграцию можно было отменить. Если reverse_code имеет значение None (по умолчанию), операцию RunPython нельзя отменить.

Необязательный аргумент hints будет передан как **hints методу allow_migrate() маршрутизаторов баз данных, чтобы помочь им выбрать маршрут. Подробнее о подсказках для баз данных см. в разделе Подсказки.

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

Рекомендуется определить функцию в файле миграции перед классом Migration и передать её в RunPython. Вот пример использования RunPython для создания начальных объектов модели Country:

from django.db import migrations


def forwards_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).bulk_create(
        [
            Country(name="USA", code="us"),
            Country(name="France", code="fr"),
        ]
    )


def reverse_func(apps, schema_editor):
    # forwards_func() creates two Country instances,
    # so reverse_func() should delete them.
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).filter(name="USA", code="us").delete()
    Country.objects.using(db_alias).filter(name="France", code="fr").delete()


class Migration(migrations.Migration):
    dependencies = []

    operations = [
        migrations.RunPython(forwards_func, reverse_func),
    ]

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

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

По умолчанию RunPython выполняет свой код внутри транзакции в базах данных, не поддерживающих транзакции DDL (например, MySQL и Oracle). Это должно быть безопасно, но может привести к сбою при попытке использовать schema_editor, предоставленный этими бэкендами. В этом случае передайте atomic=False операции RunPython.

В базах данных, поддерживающих транзакции DDL (SQLite и PostgreSQL), для операций RunPython не создаются дополнительные транзакции, кроме транзакций для каждой миграции. Поэтому, например, в PostgreSQL следует избегать сочетания изменений схемы и операций RunPython в одной миграции, иначе могут возникнуть ошибки вроде OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events.

Если вы используете другую базу данных и не уверены, поддерживает ли она транзакции DDL, проверьте атрибут django.db.connection.features.can_rollback_ddl.

Если операция RunPython входит в неатомарную миграцию, она будет выполняться в транзакции только в том случае, если параметр atomic=True передан операции RunPython.

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

RunPython не меняет автоматически подключение моделей. Любые вызываемые вами методы моделей будут обращаться к базе данных по умолчанию, если вы не укажете им текущий псевдоним базы данных (доступен через schema_editor.connection.alias, где schema_editor — второй аргумент вашей функции).

static RunPython.noop() [источник]

Передайте метод RunPython.noop в code или reverse_code, если операция не должна выполнять никаких действий в указанном направлении. Это особенно полезно для создания обратимой операции.

SeparateDatabaseAndState

class SeparateDatabaseAndState(database_operations=None, state_operations=None) [источник]

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

Она принимает два списка операций. При применении состояния будет использоваться список state_operations (это обобщённая версия аргумента state_operations операции RunSQL). При применении изменений к базе данных будет использоваться список database_operations.

Если фактическое состояние базы данных и представление Django о нём перестанут совпадать, это может нарушить работу системы миграций и даже привести к потере данных. Будьте осторожны и тщательно проверяйте операции с базой данных и состоянием. Для проверки операций с базой данных можно использовать sqlmigrate и dbshell. Для проверки операций с состоянием можно использовать makemigrations, особенно с параметром --dry-run.

Пример использования SeparateDatabaseAndState см. в разделе Изменение ManyToManyField для использования промежуточной модели.

Категория операции

class OperationCategory [исходный код]

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

ADDITION

Символ: +

REMOVAL

Символ: -

ALTERATION

Символ: ~

PYTHON

Символ: p

SQL

Символ: s

MIXED

Символ: ?

Создание собственной операции

У операций относительно простой API, и они спроектированы так, чтобы вы могли легко создавать собственные операции в дополнение к встроенным операциям Django. Базовая структура Operation выглядит следующим образом:

from django.db.migrations.operations.base import Operation


class MyCustomOperation(Operation):
    # If this is False, it means that this operation will be ignored by
    # sqlmigrate; if true, it will be run and the SQL collected for its output.
    reduces_to_sql = False

    # If this is False, Django will refuse to reverse past this operation.
    reversible = False

    # This categorizes the operation. The corresponding symbol will be
    # displayed by the makemigrations command.
    category = OperationCategory.ADDITION

    def __init__(self, arg1, arg2):
        # Operations are usually instantiated with arguments in migration
        # files. Store the values of them on self for later use.
        pass

    def state_forwards(self, app_label, state):
        # The Operation should take the 'state' parameter (an instance of
        # django.db.migrations.state.ProjectState) and mutate it to match
        # any schema changes that have occurred.
        pass

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        # The Operation should use schema_editor to apply any changes it
        # wants to make to the database.
        pass

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        # If reversible is True, this is called when the operation is reversed.
        pass

    def describe(self):
        # This is used to describe what the operation does.
        return "Custom Operation"

    @property
    def migration_name_fragment(self):
        # Optional. A filename part suitable for automatically naming a
        # migration containing this operation, or None if not applicable.
        return "custom_operation_%s_%s" % (self.arg1, self.arg2)

Вы можете взять этот шаблон за основу, хотя мы рекомендуем изучить встроенные операции Django в django.db.migrations.operations — в них приведено множество примеров использования полу-внутренних аспектов фреймворка миграций, таких как ProjectState и шаблоны получения исторических моделей, а также ModelState и шаблоны изменения исторических моделей в state_forwards().

Обратите внимание на следующее:

  • Вам не нужно слишком глубоко изучать ProjectState, чтобы писать миграции; достаточно знать, что у него есть свойство apps, предоставляющее доступ к реестру приложений (после этого можно вызвать для него get_model).
  • database_forwards и database_backwards получают по два состояния; они представляют собой изменения, которые применила бы операция state_forwards, но передаются вам для удобства и повышения производительности.
  • Если вы хотите работать с классами моделей или экземплярами моделей из аргумента from_state в database_forwards() или database_backwards(), необходимо отрендерить состояния моделей с помощью метода clear_delayed_apps_cache(), чтобы связанные модели стали доступны:

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        # This operation should have access to all models. Ensure that all models are
        # reloaded in case any are delayed.
        from_state.clear_delayed_apps_cache()
        ...
    
  • to_state в методе database_backwards — это более старое состояние, то есть состояние, которое станет текущим после завершения отката миграции.
  • Возможно, вы встретите реализации references_model во встроенных операциях; они относятся к коду автоматического обнаружения и не имеют значения для пользовательских операций.

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

В целях повышения производительности экземпляры Field в ModelState.fields повторно используются в разных миграциях. Никогда не изменяйте атрибуты этих экземпляров. Если вам нужно изменить поле в state_forwards(), удалите старый экземпляр из ModelState.fields и добавьте на его место новый. То же самое относится к экземплярам Manager в ModelState.managers.

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

from django.db.migrations.operations.base import Operation


class LoadExtension(Operation):
    reversible = True

    def __init__(self, name):
        self.name = name

    def state_forwards(self, app_label, state):
        pass

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.execute("CREATE EXTENSION IF NOT EXISTS %s" % self.name)

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        schema_editor.execute("DROP EXTENSION %s" % self.name)

    def describe(self):
        return "Creates extension %s" % self.name

    @property
    def migration_name_fragment(self):
        return "create_extension_%s" % self.name

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/ref/migration-operations/

Spec-Zone.ru

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