Миграционные операции

Файлы миграции состоят из одного или нескольких объектов, Operation которые декларативно записывают операции, которые должны быть выполнены с базой данных.

Django также использует эти объекты Operation для перекомпоновки исторического состояния ваших моделей и для расчета изменений, внесенных в модели с момента последней миграции, чтобы вы могли автоматически записывать миграции; вот почему они декларативны, что позволяет Django легко загружать их все в память и выполнять итерацию по ним, не обращаясь к базе данных, чтобы найти, как должен выглядеть проект.

Некоторые Operation более специализированные объекты предназначены для таких операций, как миграция данных и для расширенного ручного управления базой данных. Вы даже можете написать свои собственные классы, Operation если хотите учитывать часто вносимые изменения.

Если вам нужен пустой файл миграции для записи ваших собственных объектов Operation , запустите , но имейте в виду, что ручное добавление операций изменения схемы может испортить детектор автоматического переноса и вызвать неправильный код во время последующих запусков. оф .python manage.py makemigrations --empty nom_application makemigrations

Все основные операции Django находятся в модуле django.db.migrations.operations .

Вводный контент см. В тематическом руководстве по миграции .

Схема операций

CreateModel

классCreateModel ( имя , поля , параметры = Нет , базы = Нет , менеджеры = Нет )

Создает новую модель в истории проекта и соответствующую таблицу в базе данных.

name - это имя модели, записанное в файле models.py .

fields список двоичных кортежей . Экземпляр поля должен быть несвязанным полем (поэтому просто , а не полем, взятым из другой модели).(nom_champ, instance_champ) models.CharField(…)

options - необязательный словарь значений, взятых из класса Meta модели.

bases - необязательный список других классов, которые должна унаследовать эта модель; он может содержать как объекты класса, так и строки формата, "nomapp.NomModele" если вы хотите зависеть от другой модели (поэтому вы наследуете ее историческую версию). В случае отсутствия значение по умолчанию наследуется от стандартной модели models.Model .

managers принимает список двоичных кортежей . Первый менеджер в списке будет менеджером по умолчанию данной модели для миграций.(nom_gestionnaire, instance_gestionnaire)

DeleteModel

классDeleteModel ( имя )

Удаляет модель из истории проекта и ее таблицы из базы данных.

RenameModel

классRenameModel ( old_name , new_name )

Переименовывает шаблон со старого имени на новое.

Вам может потребоваться добавить эту операцию вручную, если вы измените и имя шаблона, и несколько его полей; для автодетектора будет казаться, что старая модель была удалена, а новая модель была добавлена ​​с другим именем, что приведет к потере всех данных из старой таблицы при созданной миграции.

AlterModelTable

классAlterModelTable ( название , таблица )

Изменяет имя таблицы модели (параметр db_table внутреннего класса Meta ).

AlterUniqueTogether

классAlterUniqueTogether ( имя , уникальные_всего )

Изменяет набор ограничений уникальности модели (параметр unique_together внутреннего класса Meta ).

AlterIndexTogether

классAlterIndexTogether ( имя , индекс_всего )

Изменяет набор настраиваемых индексов модели (параметр index_together внутреннего класса Meta ).

AlterOrderWithRespectTo

classAlterOrderWithRespectTo ( name , order_with_respect_to )

Создает или удаляет _order необходимый столбец для параметра order_with_respect_to внутреннего класса Meta .

AlterModelOptions

классAlterModelOptions ( имя , параметры )

Сохраняет изменения в различных параметрах модели (атрибутах класса Meta модели) как permissions или verbose_name . Не влияет на базу данных, но сохраняет эти изменения для использования экземплярами RunPython . options должен быть словарь, сопоставляющий имена параметров с их значениями.

AlterModelManagers

классAlterModelManagers ( имя , менеджеры )

Изменяет обработчики, доступные во время миграции.

AddField

классAddField ( имя_модели , имя , поле , preserve_default = True )

Добавляет поле в модель. model_name это имя шаблона, name это имя поля, и field является Field несвязанным экземпляр поля (что бы вы положили в объявлении поля в models.py , например models.IntegerField(null=True) ).

Параметр preserve_default указывает, является ли значение поля по умолчанию постоянным и должно быть включено в состояние проекта ( True ) или оно только временное и предназначено только для миграции ( False ), обычно потому, что миграция добавляет ненулевое поле в таблицу и требуется значение по умолчанию для заполнения существующих строк. Это не влияет на поведение установки значений по умолчанию в самой базе данных, поскольку Django никогда не устанавливает значения по умолчанию для базы данных и всегда применяет их на уровне кода Django ORM.

Предупреждение

В более старых базах данных добавление поля со значением по умолчанию может привести к полной перезаписи таблицы. Это происходит даже для полей с допустимым нулевым значением и может повлиять на производительность. Чтобы этого не произошло, нужно предпринять следующие шаги.

  • Добавьте поле с пустым значением, разрешенным без значения по умолчанию, и выполните команду makemigrations . Это должно вызвать миграцию, содержащую операцию AddField .
  • Добавьте в свое поле значение по умолчанию и выполните команду makemigrations . Это должно вызвать миграцию, содержащую операцию AlterField .

RemoveField

классRemoveField ( имя_модели , имя )

Удаляет поле из модели.

Имейте в виду, что в обратном случае это фактически добавление поля в модель. Операция является обратимой (за исключением любой потери данных, которая необратима), если поле допускает значение NULL или если оно имеет значение по умолчанию, которое можно использовать для заполнения воссозданного столбца. Если поле не допускает значения NULL и не имеет значения по умолчанию, операция необратима.

AlterField

классAlterField ( имя_модели , имя , поле , preserve_default = True )

Изменяет определение поля, в том числе изменения в его тип, атрибуты null , unique , db_column или другие атрибуты поля.

Параметр preserve_default указывает, является ли значение поля по умолчанию постоянным и должно ли оно быть включено в состояние проекта ( True ) или оно только временное и предназначено только для миграции ( False ), обычно потому, что миграция изменяет пустое поле на ненулевое поле в таблицу, и ему необходимо значение по умолчанию для заполнения существующих строк. Это не влияет на поведение установки значений по умолчанию в самой базе данных, поскольку Django никогда не устанавливает значения по умолчанию для базы данных и всегда применяет их на уровне кода Django ORM.

Обратите внимание, что не всегда возможно внести все изменения, в том числе в зависимости от базы данных. Например, невозможно преобразовать поле текстового типа, как models.TextField() в поле числового типа, как models.IntegerField() в большинстве баз данных.

RenameField

классRenameField ( model_name , old_name , new_name )

Изменяет имя поля (и, если оно db_column не определено, имя столбца).

AddIndex

classAddIndex ( имя_модели , индекс )

Создает индекс в таблице базы данных для модели model_name . index является экземпляром класса Index .

RemoveIndex

классRemoveIndex ( имя_модели , имя )

Удаляет именованный индекс name из модели с именем model_name .

AddConstraint

classAddConstraint ( имя_модели , ограничение )

Создает: doc: `constraint </ ref / models / constraints>` в таблице базы данных для указанной модели model_name .

RemoveConstraint

классRemoveConstraint ( имя_модели , имя )

Удаляет именованное ограничение name из модели с именем model_name .

Специальные операции

RunSQL

классRunSQL ( SQL , reverse_sql = None , state_operations = None , намеки = нет , elidable = False )

Позволяет выполнять произвольный код SQL в базе данных, что полезно для более продвинутых функций механизмов базы данных, которые Django не поддерживает напрямую.

sql , и, reverse_sql если указан, должны быть строками SQL для выполнения в базе данных. Для большинства механизмов баз данных (всех, кроме PostgreSQL) Django разделяет код SQL на отдельные операторы перед их выполнением.

Предупреждение

В PostgreSQL и SQLite используйте операторы SQL BEGIN или COMMIT только в неатомарных миграциях , чтобы не нарушать состояние транзакции Django.

Вы также можете передать список строк или двоичных кортежей. Эта последняя опция используется для передачи запросов и параметров по тому же принципу, что и для 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 выполняются, когда миграция отменена. Они должны отменять то, что делают запросы sql . Например, чтобы отменить указанную выше вставку с удалением:

migrations.RunSQL(
    sql=[("INSERT INTO musician (name) VALUES (%s);", ['Reinhardt'])],
    reverse_sql=[("DELETE FROM musician where name=%s;", ['Reinhardt'])],
)

Если reverse_sql есть None (по умолчанию), операция RunSQL необратима.

Параметр 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

КлассRunPython ( код , reverse_code = None , атомные = None , намеки = нет , elidable = False )

Запускает собственный код Python в историческом контексте. codereverse_code если он указан) должны быть исполняемыми объектами, принимающими два параметра; первый - это экземпляр, django.apps.registry.Apps содержащий историзованные модели, которые соответствуют положению операции в истории проекта, а второй - экземпляр SchemaEditor .

Параметр reverse_code вызывается при обращении миграций. Этот исполняемый объект должен отменить то, что было сделано исполняемым объектом, code чтобы эта миграция была обратимой. Если reverse_code есть None (по умолчанию), операция RunPython необратима.

Необязательный параметр 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. ,

Как 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 - второй параметр, полученный вашей функцией).

staticRunPython.noop ()

Передайте метод , RunSQL.noop чтобы code или reverse_code когда вы хотите, чтобы операция не делать ничего в данном направлении. Это особенно полезно для того, чтобы сделать операцию обратимой.

SeparateDatabaseAndState

classSeparateDatabaseAndState ( database_operations = None , state_operations = None )

Узкоспециализированная операция, позволяющая смешивать операции, связанные с аспектами модификации схемы базы данных, с операциями изменения состояния (в связи с автодетектором).

Он принимает два списка операций. На вопрос , чтобы применить изменения состояния, он использует список state_operations (это обобщенный вариант параметра state_operations в RunSQL ). Когда его просят применить изменения базы данных, он использует список database_operations .

Если текущее состояние базы данных и представление Django об этом состоянии различаются, это может нарушить систему миграции или даже привести к потере данных. Стоит быть осторожным и внимательно следить за базой данных и государственными операциями. Вы можете использовать sqlmigrate и dbshell для проверки операций с базой данных. Вы можете использовать makemigrations , особенно с опцией --dry-run , для проверки состояния операций.

Чтобы увидеть пример использования SeparateDatabaseAndState , см. Раздел Изменение ManyToManyField для использования промежуточного шаблона .

Написание пользовательских операций

Операции имеют относительно простой API и спроектированы таким образом, чтобы вы могли легко писать подклассы, дополняющие те, которые предоставляет Django. Базовая структура a 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()
        ...
    
  • to_state в методе database_backwards - старшее состояние  ; то есть тот, который будет представлять текущее состояние, когда миграция завершит работу по отмене.

  • Вы можете найти реализации references_model в операциях, предоставляемых Django; это часть кода автоопределения и не играет роли для пользовательских операций.

Предупреждение

По соображениям производительности экземпляры 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

Copyright ©2021 All rights reserved