Операции миграции
Файлы миграции состоят из одного или нескольких 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.
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.
Обратите внимание, что не все изменения возможны на всех базах данных — например, вы не можете изменить поле типа text, как 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.
Специальные операции
RunSQL
-
class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False)[source]
Позволяет выполнять произвольный SQL-код в базе данных — полезно для более сложных функций баз данных, которые Django не поддерживает напрямую, таких как частичные индексы.
sql, и reverse_sql (если указаны), должны быть строками SQL-кода, которые нужно выполнить в базе данных. В большинстве баз данных (кроме PostgreSQL), Django разделит SQL-код на отдельные инструкции перед их выполнением. Для этого требуется установить библиотеку Python sqlparse.
Вы также можете передать список строк или 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 для создания некоторых начальных объектов модели %%%CODE_BLOCK_120%%:
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.1/ref/migration-operations/