Операции миграции
Файлы миграции состоят из одного или нескольких 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) — обычно потому, что миграция добавляет поле, не допускающее NULL, в таблицу и ей нужно значение по умолчанию для существующих строк. Это не влияет на поведение установки значений по умолчанию непосредственно в базе данных — Django никогда не устанавливает значения по умолчанию в базе данных и всегда применяет их в коде Django ORM.
RemoveField
-
class RemoveField(model_name, name)[source]
Удаляет поле из модели.
Обратите внимание, что при обратном выполнении это фактически добавит поле к модели; если поле не допускает 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(), в большинстве баз данных.
Аргумент preserve_default был добавлен.
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 на отдельные операторы перед выполнением. Для этого требуется установить библиотеку 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, оно не увидит операцию, добавляющую это поле, и попытается выполнить её снова).
Необязательный аргумент 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 django.db import models, 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 не имеют автоматически добавляемых транзакций, кроме транзакций, созданных для каждой миграции (параметр 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, и паттернов получения исторических моделей.
Некоторые моменты:
- Вам не нужно слишком глубоко разбираться в
ProjectStateдля написания простых миграций; достаточно знать, что у неё есть свойствоapps, которое предоставляет доступ к реестру приложений (который вы можете вызватьget_model). -
database_forwardsиdatabase_backwardsоба получают два состояния, переданные им; эти состояния просто представляют разницу, которую методstate_forwardsприменил бы, но предоставляются для удобства и повышения скорости. -
to_stateв методе database_backwards — это старое состояние, то есть состояние, которое будет текущим после завершения отмены миграции. - Вы можете увидеть реализации
references_modelна встроенных операциях; это часть кода автообнаружения и не имеет значения для пользовательских операций.
В качестве простого примера давайте создадим операцию, которая загружает расширения 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.8/ref/migration-operations/