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

Миграционные операции

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

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

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

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

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

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

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

Создатьмодель

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). Первый менеджер в списке будет менеджером по умолчанию для этой модели во время миграции.

УдалитьМодель

class DeleteModel(name)

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

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

class RenameModel(old_name, new_name)

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

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

АльтерМодельТабле

class AlterModelTable(name, table)

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

АльтерМоделТаблеКоммент

class AlterModelTableComment(name, table_comment)

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

AlterUniqueTogether

class AlterUniqueTogether(name, unique_together)

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

АльтерИндексВместе

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.

АльтерМодельОптионс

class AlterModelOptions(name, options)

Сохраняет изменения в различных параметрах модели (настройках «Мета» модели), таких как «разрешения» и «verbose_name». Не влияет на базу данных, но сохраняет эти изменения для использования экземплярами RunPython. options должен быть словарем, сопоставляющим имена опций со значениями.

АльтерМодельМенеджеры

class AlterModelManagers(name, managers)

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

Добавить поле

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

Добавляет поле в модель. model_name — это имя модели, name — это имя поля, а 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 и не имеет значения по умолчанию, операция необратима.

PostgreSQL

RemoveField также удалит любые дополнительные объекты базы данных, связанные с удаленным полем (например, представления). Это связано с тем, что результирующий оператор DROP COLUMN будет включать предложение CASCADE, чтобы гарантировать, что зависимые объекты вне таблицы также будут удалены.

АльтерФилд

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

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

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

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

ПереименоватьПоле

class RenameField(model_name, old_name, new_name)

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

ДобавитьИндекс

class AddIndex(model_name, index)

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

УдалитьИндекс

class RemoveIndex(model_name, name)

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

ПереименоватьИндекс

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

AddConstraint

class AddConstraint(model_name, constraint)

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

Удалить ограничение

class RemoveConstraint(model_name, name)

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

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

ВыполнитьSQL

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.

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

Запустить Python

class RunPython(code, reverse_code=None, atomic=None, hints=None, elidable=False)

Запускает собственный код Python в историческом контексте. codereverse_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: невозможно ALTER TABLE «mytable», потому что у нее есть ожидающие триггерные события».

Если у вас другая база данных и вы не уверены, поддерживает ли она транзакции 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 :class:`RunSQL). Когда будет предложено применить изменения к базе данных, он будет использовать список «database_operations».

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

Пример использования SeparateDatabaseAndState см. в разделе «Изменение многих полей для использования сквозной модели».

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

New in Django 5.1.
class OperationCategory

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

ADDITION

Символ: +

REMOVAL

Символ: -

ALTERATION

Символ: ~

PYTHON

Символ: p

SQL

Символ: s

MIXED

Символ: ?

Написание своего собственного

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

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
Back to Top