Операции миграции
Файлы миграции состоят из одного или нескольких 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)[source]
Создает новую модель в истории проекта и соответствующую таблицу в базе данных для ее соответствия.
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)[source]
Удаляет модель из истории проекта и ее таблицу из базы данных.
RenameModel
-
class RenameModel(old_name, new_name)[source]
Переименовывает модель со старого имени на новое.
Возможно, вам придется добавить это вручную, если вы измените имя модели и сразу несколько связанных с ней полей; для автоматического определения это будет выглядеть так, как будто вы удалили модель со старым именем и добавили новую с другим именем, и миграция, созданная таким образом, потеряет все данные в старой таблице.
AlterModelTable
-
class AlterModelTable(name, table)[source]
Изменяет имя таблицы модели (опция db_table в классе Meta).
AlterUniqueTogether
-
class AlterUniqueTogether(name, unique_together)[source]
Изменяет набор уникальных ограничений модели (опция unique_together в классе Meta).
AlterIndexTogether
-
class AlterIndexTogether(name, index_together)[source]
Изменяет набор пользовательских индексов модели (опция index_together в классе Meta).
AlterOrderWithRespectTo
-
class AlterOrderWithRespectTo(name, order_with_respect_to)[source]
Добавляет или удаляет столбец _order , необходимый для опции order_with_respect_to в классе Meta.
AlterModelOptions
-
class AlterModelOptions(name, options)[source]
Хранит изменения в различных параметрах модели (настройки в классе Meta модели), таких как permissions и verbose_name . Не влияет на базу данных, но сохраняет эти изменения для использования экземплярами RunPython. options должен быть словарем, сопоставляющим имена параметров значениям.
AlterModelManagers
-
class AlterModelManagers(name, managers)[source]
Изменяет доступные менеджеры во время миграции.
AddField
-
class AddField(model_name, name, field, preserve_default=True)[source]
Добавляет поле в модель. 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)[source]
Удаляет поле из модели.
Помните, что при обратном выполнении это фактически добавление поля в модель. Операция обратима (кроме потери данных, которая, конечно, необратима), если поле имеет значение NULL или если у него есть значение по умолчанию, которое можно использовать для заполнения вновь созданного столбца. Если поле не допускает NULL и не имеет значения по умолчанию, операция необратима.
AlterField
-
class AlterField(model_name, name, field, preserve_default=True)[source]
Изменяет определение поля, включая изменения типа, 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)[source]
Изменяет имя поля (и, если не задано db_column, его имя столбца).
AddIndex
-
class AddIndex(model_name, index)[source]
Создает индекс в таблице базы данных для модели с model_name. index является экземпляром класса Index.
RemoveIndex
-
class RemoveIndex(model_name, name)[source]
Удаляет индекс с именем name из модели с model_name.
AddConstraint
-
class AddConstraint(model_name, constraint)[source]
Создает ограничение в таблице базы данных для модели с model_name.
RemoveConstraint
-
class RemoveConstraint(model_name, name)[source]
Удаляет ограничение с именем name из модели с model_name.
Специальные операции
RunSQL
-
class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False)[source]
Позволяет выполнять произвольный SQL-код в базе данных — полезно для более сложных функций баз данных, которые Django не поддерживает напрямую, таких как частичные индексы.
sql, и reverse_sql, если указаны, должны быть строками SQL-кода для выполнения в базе данных. В большинстве баз данных (кроме PostgreSQL) Django разделит SQL на отдельные инструкции перед их выполнением.
Вы также можете передать список строк или 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 запросы выполняются при отмене миграции, чтобы вы могли отменить изменения, сделанные в прямых запросах:
migrations.RunSQL(
[("INSERT INTO musician (name) VALUES (%s);", ['Reinhardt'])],
[("DELETE FROM musician where name=%s;", ['Reinhardt'])],
)
Аргумент 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)[source]
Выполняет пользовательский Python-код в историческом контексте. code (и reverse_code, если указаны) должны быть вызываемыми объектами, принимающими два аргумента; первый — экземпляр django.apps.registry.Apps, содержащий исторические модели, которые соответствуют положению операции в истории проекта, а второй — экземпляр SchemaEditor.
Аргумент reverse_code вызывается при отмене миграций. Этот вызываемый объект должен отменять то, что делает вызываемый объект code, чтобы миграция была обратимой.
Необязательный аргумент 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-коду.
Если вы обновляете с South, это по сути шаблон South как операция — один или два метода для прямого и обратного преобразования, с доступным ORM и операциями со схемой. В большинстве случаев вы сможете напрямую перевести ссылки orm.Model или orm["appname", "Model"] из South в ссылки apps.get_model("appname", "Model") и оставить большую часть остального кода неизменной для миграций данных. Однако, apps будет содержать только ссылки на модели в текущем приложении, если к зависимостям миграции не добавлены миграции в других приложениях.
Как и 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()[source] -
Передайте метод
RunPython.noopвcodeилиreverse_code, если вы хотите, чтобы операция ничего не делала в заданном направлении. Это особенно полезно для обратимости операции.
SeparateDatabaseAndState
-
class SeparateDatabaseAndState(database_operations=None, state_operations=None)[source]
Высокоспециализированная операция, которая позволяет смешивать и сопоставлять аспекты базы данных (изменение схемы) и состояния (активация автодетектора) операций.
Она принимает два списка операций, и при запросе на применение состояния будет использовать список состояния, а при запросе на применение изменений в базе данных — список базы данных. Не используйте эту операцию, если вы не уверены в том, что знаете, что делаете.
Создание собственных операций
Операции имеют относительно простой 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/2.2/ref/migration-operations/