Операции миграции
Файлы миграции состоят из одного или нескольких 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).
AlterUniqueTogether
-
class AlterUniqueTogether(name, unique_together)
Изменяет набор уникальных ограничений модели (опцию unique_together в подклассе Meta).
AlterIndexTogether
-
class AlterIndexTogether(name, index_together)
Изменяет набор пользовательских индексов модели (опцию index_together в подклассе Meta).
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.
Предупреждение
В старых базах данных добавление поля со значением по умолчанию может привести к полной перезаписи таблицы. Это происходит даже для nullable полей и может негативно повлиять на производительность. Чтобы этого избежать, следует выполнить следующие шаги.
- Добавьте nullable поле без значения по умолчанию и выполните команду
makemigrations. Это должно сгенерировать миграцию с операциейAddField. - Добавьте значение по умолчанию в ваше поле и выполните команду
makemigrations. Это должно сгенерировать миграцию с операциейAlterField.
RemoveField
-
class RemoveField(model_name, name)
Удаляет поле из модели.
Помните, что при обращении эта операция фактически добавляет поле к модели. Операция обратима (кроме потери данных, которая необратима), если поле nullable или имеет значение по умолчанию, которое можно использовать для заполнения воссозданного столбца. Если поле не nullable и не имеет значения по умолчанию, операция необратима.
AlterField
-
class AlterField(model_name, name, field, preserve_default=True)
Изменяет определение поля, включая изменения его типа, null, unique, db_column и других атрибутов поля.
Аргумент preserve_default указывает, является ли значение по умолчанию поля постоянным и должно быть включено в состояние проекта (True), или временным и только для этой миграции (False) — обычно, когда миграция изменяет nullable поле на не nullable и ей нужно значение по умолчанию, чтобы поместить его в существующие строки. Это не влияет на поведение установки значений по умолчанию в базе данных напрямую — Django никогда не устанавливает значения по умолчанию в базе данных и всегда применяет их в коде Django ORM.
Обратите внимание, что не все изменения возможны во всех базах данных — например, вы не можете изменить поле типа text, такое как 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.
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 (это обобщённая версия аргумента 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)
Добавлен атрибут migration_name_fragment.
Вы можете использовать этот шаблон как основу, хотя мы рекомендуем изучить встроенные операции 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/3.2/ref/migration-operations/