Spec-Zone.ru › Django 5.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 — это список пар кортежей (field_name, field_instance). Экземпляр поля должен быть свободным полем (только models.CharField(...), а не полем, взятым из другой модели).

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

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

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

DeleteModel

class DeleteModel(name)

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

RenameModel

class RenameModel(old_name, new_name)

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

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

AlterModelTable

class AlterModelTable(name, table)

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

AlterModelTableComment

Новое в Django 4.2.
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 — экземпляр поля (то, что вы поместите в объявление поля в models.py - например, models.IntegerField(null=True)).

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

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

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

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

RemoveField

class RemoveField(model_name, name)

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

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

AlterField

class AlterField(model_name, name, field, preserve_default=True)

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

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

END_OF_DOCUMENT_MARKER

Обратите внимание, что не все изменения возможны на всех базах данных — например, вы не можете изменить поле текстового типа, как 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.

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

AddConstraint

class AddConstraint(model_name, constraint)

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

RemoveConstraint

class RemoveConstraint(model_name, name)

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

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

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 используйте только BEGIN или COMMIT в своём SQL-коде в неатомных миграциях, чтобы избежать нарушения состояния транзакции 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 (это обобщенная версия аргумента RunSQL). При применении изменений в базе данных она будет использовать список database_operations.

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

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

Написание собственной операции

Операции имеют относительно простой 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

    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 in console output.
        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/5.0/ref/migration-operations/

Spec-Zone.ru

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