Операции миграции
Файлы миграции состоят из одного или нескольких 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, его имя столбца).
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, если вы хотите, чтобы операция ничего не делала в заданном направлении. Это особенно полезно для обратимости операции.
Был добавлен аргумент 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
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. Базовая структура собственной операции выглядит так:
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/1.11/ref/migration-operations/