Операции миграции
Файлы миграции состоят из одного или нескольких 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 — объект поля (то, что вы бы поместили в объявление поля в 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 и не имеет значения по умолчанию, операция необратима.
AlterField
-
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(), в большинстве баз данных.
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"
Вы можете взять эту шаблон и работать с ним, хотя мы рекомендуем изучить встроенные операции 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
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.0/ref/migration-operations/