Операции миграции
Файлы миграции состоят из одного или нескольких 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.
Обратите внимание, что не все изменения возможны во всех базах данных — например, вы не можете изменить поле типа «текст», такое как 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.
Также можно передать список строк или кортежей из двух элементов. Последнее используется для передачи запросов и параметров так же, как в 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() ...Новое в Django 1.11:Это требование и метод
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.0/ref/migration-operations/