Операции миграции
Файлы миграций состоят из одного или нескольких 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 — список пар вида «ключ-значение» (field_name, field_instance). Объект поля должен быть свободным полем (то есть, просто models.CharField(...), а не поле, взятое из другой модели).
options — необязательный словарь значений из класса Meta модели.
bases — необязательный список других классов, от которых должна наследоваться модель; он может содержать как объекты класса, так и строки в формате "appname.ModelName", если вы хотите зависеть от другой модели (т.е. унаследовать от исторической версии). Если не указано, по умолчанию используется стандартный класс models.Model.
managers принимает список пар вида «ключ-значение» (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) — обычно, потому что миграция добавляет непустое поле в таблицу и нуждается в значении по умолчанию для заполнения существующих строк. Это не влияет на поведение установки значений по умолчанию непосредственно в базе данных — 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.
Обратите внимание, что не все изменения возможны во всех базах данных — например, вы не можете изменить поле типа «текст», как 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, 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, если вы хотите, чтобы операция ничего не делала в заданном направлении. Это особенно полезно для обратимости операции.
Был добавлен аргумент elidable.
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.
# -*- 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 не имеют автоматически добавляемых транзакций помимо транзакций, созданных для каждой миграции. Таким образом, в 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, если вы хотите, чтобы операция ничего не делала в заданном направлении. Это особенно полезно для обратимости операции.
Был добавлен аргумент elidable.
Значение по умолчанию аргумента atomic было изменено на None, указывая, что атомарность контролируется атрибутом atomic миграции.
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.10/ref/migration-operations/