# Django 3.2.11.dev 文档

# 迁移操作

迁移文件由一个或多个 `Operation` 组成，这些对象声明性地记录了迁移对数据库的作用。

Django 也使用这些 `Operation` 对象来计算出你的模型在历史上的样子，并计算出自上次迁移以来你对模型做了哪些改变，这样它就可以自动写出你的迁移；这就是为什么它们是声明式的，因为这意味着 Django 可以很容易地将它们全部加载到内存中，并在不接触数据库的情况下运行它们，以计算出你的项目应该是什么样子。

还有更专业的 `Operation` 对象，用于诸如 数据迁移 和进阶的手动数据库操作。如果你想封装你经常进行的自定义更改，你也可以编写自己的 `Operation` 类。

如果你需要一个空的迁移文件来编写你自己的 `Operation` 对象，使用 `python manage.py makemigrations --empty yourappname`，但是要注意手动添加架构变化的操作会混淆迁移自动检测器，使 `makemigrations` 的运行结果输出错误的代码。

所有的 Django 核心操作都可以在 `django.db.migrations.options` 模块中找到。

关于介绍性材料，见 迁移专题指南。

## 架构操作

### `CreateModel`

class `CreateModel`(name, fields, options=None, bases=None, managers=None)

在项目历史中创建一个新的模型，并在数据库中创建与之匹配的相应表。

`name` 是模型名称，如在 `models.py` 文件中写的那样。

`fields` 是一个由 `(field_name, field_instance)` 组成的2二元元组的列表。字段实例应该是一个未绑定的字段（所以只是 `models.CharField(...)`，而不是取自另一个模型的字段）。

`options` 是模型 `Meta` 类的可选值字典。

`bases` 是一个可选的其他类的列表，让这个模型继承；它既可以包含类对象，也可以包含格式为 `"appname.ModelName"` 的字符串，如果你想依赖另一个模型（所以你继承了历史版本）。如果没有提供，它默认为从标准的 `models.Model` 继承。

`managers` 需要一个由 `(manager_name, manager_instance)` 组成的二元元组的列表。在迁移过程中，列表中的第一个管理器将是这个模型的默认管理器。

### `DeleteModel`

class `DeleteModel`(name)

从项目历史中删除模型，并从数据库中删除它的表。

### `RenameModel`

class `RenameModel`(old_name, new_name)

将模型从旧名称改名为新名称。

如果你一次更改了模型的名称和相当多的字段，你可能必须手动添加这个；对自动检测器来说，这看起来就像你删除了一个使用旧名称的模型，并添加了一个使用不同名称的新模型，它创建的迁移将丢失旧表中的任何数据。

### `AlterModelTable`

class `AlterModelTable`(name, table)

更改模型的表名（`Meta` 子类上的 `db_table` 选项）。

### `AlterUniqueTogether`

class `AlterUniqueTogether`(name, unique_together)

改变模型的唯一约束集（`Meta` 子类上的 `unique_together` 选项）。

### `AlterIndexTogether`

class `AlterIndexTogether`(name, index_together)

更改模型的自定义索引集（`Meta` 子类上的 `index_together` 选项）。

### `AlterOrderWithRespectTo`

class `AlterOrderWithRespectTo`(name, order_with_respect_to)

生成或删除 `Meta` 子类上的 `order_with_respect_to` 选项所需的 `_order` 列。

### `AlterModelOptions`

class `AlterModelOptions`(name, options)

存储对各种模型选项的更改（模型 `Meta` 上的设置），如 `permissions` 和 `verbose_name`。不影响数据库，但会将这些更改持久化，供 `RunPython` 实例使用。`options` 应该是一个将选项名映射到值的字典。

### `AlterModelManagers`

class `AlterModelManagers`(name, managers)

改变迁移期间可用的管理器。

### `AddField`

class `AddField`(model_name, name, field, preserve_default=True)

为模型添加一个字段。`model_name` 是模型的名称，`name` 是字段的名称，`field` 是一个未绑定的字段实例（你会在 `models.py` 中的字段声明中放入的东西——例如，`models.IntegerField(null=True)`）。

`preserve_default` 参数表示字段的默认值是否是永久的，应该被内置到项目状态中（`True`），或者它是否是临时的，只适用于这次迁移（`False`）——通常是因为迁移在表中添加一个不可空的字段，需要一个默认值放到现有的行中。它不影响直接在数据库中设置默认值的行为——Django 从不设置数据库默认值，而总是在 Django ORM 代码中应用。

警告

在旧的数据库中，添加一个具有默认值的字段可能会导致表的完全重写。即使是对于可空字段，也会发生这种情况，可能会对性能产生负面影响。为了避免这种情况，应采取以下步骤。

- 添加可空字段，不含默认值，并运行 `makemigrations` 命令。这应该会产生一个带有 `AddField` 操作的迁移。
- 将默认值添加到你的字段中，然后运行:djadmin:makemigrations 命令。这应该会生成一个带有 `AlterField` 操作的迁移。

### `RemoveField`

class `RemoveField`(model_name, name)

从模型中删除一个字段。

请记住，当反向迁移时，这实际上是向模型添加一个字段。如果字段是可空的，或者它有一个默认值，可以用来填充重新创建的列，那么这个操作是可逆的（除了任何数据损失，这是不可逆的）。如果字段不可为空，也没有默认值，则该操作是不可逆的。

### `AlterField`

class `AlterField`(model_name, name, field, preserve_default=True)

改变字段的定义，包括改变其类型、`null`、`unique`、`db_column` 等字段属性。

`preserve_default` 参数表示字段的默认值是否是永久的，应该被内置到项目状态中（`True`），或者它是否是临时的，只适用于这次迁移（`False`）——通常是因为迁移将一个可空的字段改变为不可空的字段，需要一个默认值放到现有的行中。它不影响直接在数据库中设置默认值的行为——Django 从不设置数据库默认值，而总是在 Django ORM 代码中应用。

请注意，并不是所有的数据库都可以进行所有的更改——例如，在大多数数据库中，你不能将像 `models.TextField()` 这样的文本型字段改为像 `models.IntegerField()` 这样的数字型字段。

### `RenameField`

class `RenameField`(model_name, old_name, new_name)

改变一个字段的名称（除非设置了 `db_column`，否则改变其列名）。

### `AddIndex`

class `AddIndex`(model_name, index)

在数据库表中为模型创建一个带有 `model_name` 的索引。index 是 `Index` 类的一个实例。

### `RemoveIndex`

class `RemoveIndex`(model_name, name)

从带有 `model_name` 的模型中删除名为 `name` 的索引。

### `AddConstraint`

class `AddConstraint`(model_name, constraint)

在数据库表中为带有 `model_name` 的模型创建一个 约束。

### `RemoveConstraint`

class `RemoveConstraint`(model_name, name)

从带有 `model_name` 的模型中删除名为 `name` 的约束。

## 特殊操作

### `RunSQL`

class `RunSQL`(sql, reverse_sql=None, state_operations=None, hints=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` 参数决定了当 压缩迁移 时，是否会删除（elided）该操作。

`RunSQL.``noop`

  当你希望操作在给定的方向上不做任何事情时，将 `RunSQL.noop` 属性传递给 `sql` 或 `reverse_sql`。这在使操作可逆时特别有用。

### `RunPython`

class `RunPython`(code, reverse_code=None, atomic=None, hints=None, elidable=False)

在历史上下文中运行自定义 Python 代码。`code` （如果提供了 `reverse_code`）应该是可调用的对象，接受两个参数；第一个是 `django.app.registry.Apps` 的实例，包含与操作在项目历史中的位置相匹配的历史模型，第二个是 `SchemaEditor` 的实例。

`reverse_code` 参数在取消应用迁移时被调用。这个可调用对象参数应该撤销在 `code` 可调用对象参数中所做的事情，这样迁移才是可逆的。如果 `reverse_code` 是``None`` （默认），则 `RunPython` 操作是不可逆的。

可选的 `hints` 参数将作为 `**hints` 传递给数据库路由器的 `allow_migrate()` 方法，以帮助它们做出路由决策。参见 提示 了解更多关于数据库提示的细节。

可选的 `elidable` 参数决定了当 压缩迁移 时，是否会删除（elided）该操作。

建议你把这些代码写成一个单独的函数，放在迁移文件中的 `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` 是你函数的第二个参数）。

static `RunPython.``noop`()

  当你希望操作在给定的方向上不做任何事情时，将 `RunPython.noop` 方法传递给 `code` 或 `reverse_code`。这在使操作可逆时特别有用。

### `SeparateDatabaseAndState`

class `SeparateDatabaseAndState`(database_operations=None, state_operations=None)

一个高度专业化的操作，让你混合和匹配数据库（架构改变）和状态（自动检测器支持）方面的操作。

它接受两个操作列表。当要求它应用状态时，它将使用 `state_operations` 列表（这是 `RunSQL` 的 `state_operations` 参数的通用版本）。当要求它对数据库进行更改时，它将使用 `database_operations` 列表。

如果数据库的实际状态和 Django 的状态视图不同步，就会破坏迁移框架，甚至导致数据丢失。值得谨慎行事，仔细检查你的数据库和状态操作。你可以使用 `sqlmigrate` 和 `dbshell` 来检查你的数据库操作。你可以使用 `makemigrations`，特别是使用 `--dry-run`，来检查你的状态操作。

关于使用 `SeparateDatabaseAndState` 的例子，请参见 changing-a-manytomanyfield to use-a-through-model。

## 自己写

操作有一个相对简单的 API，而且它们被设计成可以让你很容易地编写自己的 API 来补充内置的 Django 的 API。一个 `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"

        @property
        def migration_name_fragment(self):
            # Optional. A filename part suitable for automatically naming a
            # migration containing this operation, or None if not applicable.
            return "custom_operation_%s_%s" % (self.arg1, self.arg2)

New in Django 3.2:

增加了 `migration_name_fragment` 属性。

你可以使用这个模板，并在此基础上进行工作，不过我们建议查看 `django.db.migrations.options` 中内置的 Django 操作——它们涵盖了很多迁移框架半内部方面的例子，比如 `ProjectState` 和用于获取历史模型的模式，以及 `ModelState` 和 `state_forwards()` 中用于突变历史模型的模式。

注意事项：

- 你不需要学习太多关于 `ProjectState` 的知识来编写迁移；只需要知道它有一个 `apps` 属性，它提供了对应用程序注册表的访问（然后你可以调用 `get_model`）。

- `database_forwards` 和 `database_backwards` 都有两个状态传递给它们；这些状态代表了 `state_forwards` 方法本会应用的差异，但为了方便和速度的原因，给了你。

- 如果你想从 `database_forwards()` 或 `database_backwards()` 中的 `from_state` 参数中处理模型类或模型实例，你必须使用 `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()
          ...

- database_backwards 方法中的 `to_state` 是指 旧 的状态；也就是反向迁移完成后将成为当前状态的状态。

- 你可能会在内置操作上看到 `references_model` 的实现，这是自动检测代码的一部分，对自定义操作并不重要。

警告

由于性能原因，`ModelState.fields.Field` 中的 `Field`` 实例会在不同的迁移中重复使用。你决不能改变这些实例的属性。如果你需要在 `state_forwards()` 中突变一个字段，你必须从 `ModelState.fields` 中删除旧的实例，并在其位置上添加一个新的实例。对于 `ModelState.managers` 中的 `Manager`` 实例也是如此。

举个例子，让我们做一个加载 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

        @property
        def migration_name_fragment(self):
            return "create_extension_%s" % self.name