Операции миграции
Файлы миграции состоят из одного или нескольких 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) . Первый менеджер в списке будет менеджером по умолчанию для этой модели во время миграций.
Аргумент managers был добавлен.
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) — обычно потому, что миграция добавляет поле, не допускающее пустых значений, в таблицу и ей нужно значение по умолчанию для заполнения существующих строк. Оно не влияет на поведение установки значений по умолчанию непосредственно в базе данных — Django никогда не устанавливает значения по умолчанию в базе данных и всегда применяет их в коде Django ORM.
RemoveField
-
class RemoveField(model_name, name)[source]
Удаляет поле из модели.
Помните, что при обратном выполнении это фактически добавит поле в модель. Операция обратима (кроме потери данных, которая, конечно же, необратима), если поле допускает пустые значения или если оно имеет значение по умолчанию, которое можно использовать для заполнения вновь созданного столбца. Если поле не допускает пустых значений и не имеет значения по умолчанию, операция необратима.
AlterField
-
class AlterField(model_name, name, field, preserve_default=True)[source]
Изменяет определение поля, включая изменения его типа, null, unique, db_column и других атрибутов поля.
Аргумент preserve_default указывает, является ли значение по умолчанию поля постоянным и должно быть включено в состояние проекта (True), или оно временное и только для этой миграции (False) — обычно потому, что миграция изменяет поле, допускающее пустые значения, на поле, не допускающее пустых значений, и ей нужно значение по умолчанию для заполнения существующих строк. Оно не влияет на поведение установки значений по умолчанию непосредственно в базе данных — Django никогда не устанавливает значения по умолчанию в базе данных и всегда применяет их в коде Django ORM.
Обратите внимание, что не все изменения возможны на всех базах данных — например, на большинстве баз данных вы не можете изменить текстовое поле, такое как models.TextField(), на числовое поле, такое как models.IntegerField().
RenameField
-
class RenameField(model_name, old_name, new_name)[source]
Изменяет имя поля (и, если не задано db_column, имя столбца).
Специальные операции
RunSQL
-
class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None)[source]
Позволяет выполнять произвольный SQL-код в базе данных — полезно для более продвинутых функций баз данных, которые Django не поддерживает напрямую, например, для частичных индексов.
sql, и reverse_sql (если указаны), должны быть строками SQL-кода для выполнения в базе данных. В большинстве баз данных (кроме PostgreSQL) Django разделит SQL-код на отдельные операторы перед выполнением. Для этого требуется установить библиотеку sqlparse Python.
Вы также можете передать список строк или кортежей из двух элементов. Последнее используется для передачи запросов и параметров так же, как в 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() роутеров баз данных для помощи им в принятии решений о маршрутизации. Подробнее см. Подсказки.
Добавлена возможность передачи параметров в sql и reverse_sql запросы.
Добавлен аргумент hints.
-
RunSQL.noop -
Передайте атрибут
RunSQL.noopвsqlилиreverse_sql, если вы хотите, чтобы операция ничего не делала в заданном направлении. Это особенно полезно для обратимости операции.
RunPython
-
class RunPython(code, reverse_code=None, atomic=True, hints=None)[source]
Выполняет пользовательский Python-код в историческом контексте. code (и reverse_code, если указан) должны быть вызываемыми объектами, принимающими два аргумента: первый — экземпляр django.apps.registry.Apps с историческими моделями, соответствующими месту операции в истории проекта, а второй — экземпляр SchemaEditor.
Аргумент reverse_code вызывается при отмене миграций. Этот вызываемый объект должен отменить то, что делается в вызываемом объекте code, чтобы миграция была обратимой.
Необязательный аргумент hints будет передан как **hints методу allow_migrate() роутеров баз данных для помощи им в принятии решения о маршрутизации. Подробнее см. Подсказки.
Добавлен аргумент hints.
Рекомендуется писать код в виде отдельной функции над классом Migration в файле миграции и просто передавать его в RunPython. Вот пример использования RunPython для создания начальных объектов модели Country.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
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 не имеют автоматически добавляемых транзакций помимо транзакций, созданных для каждой миграции (параметр atomic не влияет на эти базы данных). Таким образом, например, в PostgreSQL следует избегать объединения изменений схемы и операций RunPython в одной миграции, чтобы не столкнуться с ошибками типа OperationalError: cannot ALTER TABLE
"mytable" because it has pending trigger events.
Если у вас другая база данных и вы не уверены, поддерживает ли она транзакции DDL, проверьте атрибут django.db.connection.features.can_rollback_ddl.
Предупреждение
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применил бы, но предоставляются для удобства и скорости. -
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/1.9/ref/migration-operations/