Написание миграций базы данных
Этот документ объясняет, как структурировать и писать миграции базы данных для различных сценариев, с которыми вы можете столкнуться. Для вводной информации о миграциях см. руководство по теме.
Миграции данных и несколько баз данных
При использовании нескольких баз данных вам, возможно, придется определить, следует ли запускать миграцию на конкретной базе данных. Например, вы можете захотеть запустить миграцию только на определённой базе данных.
Для этого вы можете проверить псевдоним подключения к базе данных внутри операции RunPython , обратившись к атрибуту schema_editor.connection.alias.
from django.db import migrations
def forwards(apps, schema_editor):
if not schema_editor.connection.alias == 'default':
return
# Your migration code goes here
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(forwards),
]
Вы также можете предоставить подсказки, которые будут переданы методу allow_migrate() маршрутизаторов баз данных в виде **hints:
class MyRouter(object):
def allow_migrate(self, db, app_label, model_name=None, **hints):
if 'target_db' in hints:
return db == hints['target_db']
return True
Затем, чтобы использовать это в своих миграциях, сделайте следующее:
from django.db import migrations
def forwards(apps, schema_editor):
# Your migration code goes here
...
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(forwards, hints={'target_db': 'default'}),
]
Если ваша операция RunPython или RunSQL затрагивает только одну модель, рекомендуется передать model_name в качестве подсказки, чтобы сделать её работу как можно более прозрачной для маршрутизатора. Это особенно важно для многоразового использования и сторонних приложений.
Миграции, добавляющие уникальные поля
Применение «простой» миграции, добавляющей уникальное обязательное поле в таблицу с существующими строками, вызовет ошибку, так как значение, используемое для заполнения существующих строк, генерируется только один раз, что нарушает уникальное ограничение.
Поэтому необходимо выполнить следующие действия. В этом примере мы добавим обязательное UUIDField с заданным значением по умолчанию. Измените соответствующее поле в соответствии с вашими потребностями.
- Добавьте поле в свою модель с помощью
default=uuid.uuid4иunique=Trueаргументов (выберите подходящее значение по умолчанию для типа поля, которое вы добавляете). - Запустите команду
makemigrations. Это должно сгенерировать миграцию с операциейAddField. - Создайте два пустых файла миграций для того же приложения, дважды выполнив
makemigrations myapp --empty. Мы переименовали файлы миграций, чтобы придать им осмысленные имена в примерах ниже. -
Скопируйте операцию
AddFieldиз сгенерированной миграции (первый из трёх новых файлов) в последнюю миграцию и изменитеAddFieldнаAlterField. Например:# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models import uuid class Migration(migrations.Migration): dependencies = [ ('myapp', '0005_populate_uuid_values'), ] operations = [ migrations.AlterField( model_name='mymodel', name='uuid', field=models.UUIDField(default=uuid.uuid4, unique=True), ), ] -
Отредактируйте первый файл миграции. Сгенерированный класс миграции должен выглядеть примерно так:
class Migration(migrations.Migration): dependencies = [ ('myapp', '0003_auto_20150129_1705'), ] operations = [ migrations.AddField( model_name='mymodel', name='uuid', field=models.UUIDField(default=uuid.uuid4, unique=True), ), ]Измените
unique=Trueнаnull=True— это создаст промежуточное поле с null и отложит создание уникального ограничения до тех пор, пока мы не заполним уникальные значения во всех строках. -
В первом пустом файле миграции добавьте операцию
RunPythonилиRunSQLдля генерации уникального значения (UUID в примере) для каждой существующей строки. Например:# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models import uuid def gen_uuid(apps, schema_editor): MyModel = apps.get_model('myapp', 'MyModel') for row in MyModel.objects.all(): row.uuid = uuid.uuid4() row.save() class Migration(migrations.Migration): dependencies = [ ('myapp', '0004_add_uuid_field'), ] operations = [ # omit reverse_code=... if you don't want the migration to be reversible. migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop), ] -
Теперь вы можете применить миграции как обычно с помощью команды
migrate.Обратите внимание, что существует проблема гонки, если вы разрешаете создание объектов во время выполнения этой миграции. Объекты, созданные после
AddFieldи доRunPython, будут иметь свои оригинальныеuuidперезаписанные.
Управление порядком миграций
Django определяет порядок применения миграций не по имени файла каждой миграции, а путем построения графа, используя два свойства в классе Migration: dependencies и run_before.
Если вы использовали команду makemigrations, вы, вероятно, уже видели dependencies в действии, так как автоматически созданные миграции имеют это определено как часть процесса создания.
Свойство dependencies объявляется следующим образом:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('myapp', '0123_the_previous_migration'),
]
Обычно этого будет достаточно, но иногда вам может потребоваться гарантировать, что ваша миграция выполняется *до* других миграций. Это полезно, например, для того, чтобы миграции сторонних приложений выполнялись *после* вашего замещения AUTH_USER_MODEL.
Для этого поместите все миграции, которые должны зависеть от вашей, в атрибут run_before в вашем классе Migration:
class Migration(migrations.Migration):
...
run_before = [
('third_party_app', '0001_do_awesome'),
]
По возможности используйте dependencies вместо run_before. Вы должны использовать run_before только в том случае, если указать dependencies в миграции, которую вы хотите выполнить после той, которую вы пишете, нежелательно или непрактично.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/howto/writing-migrations/