---
title: "迁移操作"
version: 5.1
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/5.1/ref/migration-operations/
canonical: https://djangodocs.dev/zh-hans/5.1/ref/migration-operations/
---
# 迁移操作

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

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

还有更专业的 `Operation` 对象，用于诸如 [数据迁移](/zh-hans/5.1/topics/migrations/#data-migrations) 和进阶的手动数据库操作。如果你想封装你经常进行的自定义更改，你也可以编写自己的 `Operation` 类。

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

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

关于介绍性材料，见 [迁移专题指南](/zh-hans/5.1/topics/migrations/)。

## 架构操作

### `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`](/zh-hans/5.1/ref/models/options/#django.db.models.Options.db_table) 选项）。

### `AlterModelTableComment`

#### `class AlterModelTableComment(name, table_comment)`

更改模型的表注释（位于 `Meta` 子类上的 [`db_table_comment`](/zh-hans/5.1/ref/models/options/#django.db.models.Options.db_table_comment) 选项）。

### `AlterUniqueTogether`

#### `class AlterUniqueTogether(name, unique_together)`

改变模型的唯一约束集（`Meta` 子类上的 [`unique_together`](/zh-hans/5.1/ref/models/options/#django.db.models.Options.unique_together) 选项）。

### `AlterIndexTogether`

#### `class AlterIndexTogether(name, index_together)`

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

> **Warning**
>
> `AlterIndexTogether` 官方上仅支持用于 Django 4.2 之前的迁移文件。出于向后兼容的原因，它仍然是公共 API 的一部分，没有计划弃用或移除它，但不应在新的迁移中使用它。而应该使用 [`AddIndex`](#django.db.migrations.operations.AddIndex) 和 [`RemoveIndex`](#django.db.migrations.operations.RemoveIndex) 操作。

### `AlterOrderWithRespectTo`

#### `class AlterOrderWithRespectTo(name, order_with_respect_to)`

生成或删除 `Meta` 子类上的 [`order_with_respect_to`](/zh-hans/5.1/ref/models/options/#django.db.models.Options.order_with_respect_to) 选项所需的 `_order` 列。

### `AlterModelOptions`

#### `class AlterModelOptions(name, options)`

存储对各种模型选项的更改（模型 `Meta` 上的设置），如 `permissions` 和 `verbose_name`。不影响数据库，但会将这些更改持久化，供 [`RunPython`](#django.db.migrations.operations.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 代码中应用。

> **Warning**
>
> 在旧的数据库中，添加一个具有默认值的字段可能会导致表的完全重写。即使是对于可空字段，也会发生这种情况，可能会对性能产生负面影响。为了避免这种情况，应采取以下步骤。
>
> - 添加可空字段，不含默认值，并运行 [`makemigrations`](/zh-hans/5.1/ref/django-admin/#django-admin-makemigrations) 命令。这应该会产生一个带有 `AddField` 操作的迁移。
> - 将默认值添加到你的字段中，然后运行:djadmin:makemigrations 命令。这应该会生成一个带有 `AlterField` 操作的迁移。

### `RemoveField`

#### `class RemoveField(model_name, name)`

从模型中删除一个字段。

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

> **PostgreSQL**
>
> “RemoveField” 还将删除与已删除字段相关的任何其他数据库对象（例如视图）。 这是因为生成的“DROP COLUMN”语句将包含“CASCADE”子句，以确保“表外部的依赖对象也被删除”。

### `AlterField`

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

改变字段的定义，包括改变其类型、[`null`](/zh-hans/5.1/ref/models/fields/#django.db.models.Field.null)、[`unique`](/zh-hans/5.1/ref/models/fields/#django.db.models.Field.unique)、[`db_column`](/zh-hans/5.1/ref/models/fields/#django.db.models.Field.db_column) 等字段属性。

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

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

### `RenameField`

#### `class RenameField(model_name, old_name, new_name)`

改变一个字段的名称（除非设置了 [`db_column`](/zh-hans/5.1/ref/models/fields/#django.db.models.Field.db_column)，否则改变其列名）。

### `AddIndex`

#### `class AddIndex(model_name, index)`

在数据库表中为模型创建一个带有 `model_name` 的索引。index 是 [`Index`](/zh-hans/5.1/ref/models/indexes/#django.db.models.Index) 类的一个实例。

### `RemoveIndex`

#### `class RemoveIndex(model_name, name)`

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

### `RenameIndex`

#### `class RenameIndex(model_name, new_name, old_name=None, old_fields=None)`

重命名数据库中具有 `model_name` 的模型的索引。只能提供 `old_name` 和 `old_fields` 中的一个。`old_fields` 是一个字符串的可迭代对象，通常对应于 `index_together` 的字段（Django 5.1 之前的选项）。

对于不支持索引重命名语句的数据库（如 SQLite 和 MariaDB \< 10.5.2），该操作将删除并重新创建索引，这可能会很昂贵。

### `AddConstraint`

#### `class AddConstraint(model_name, constraint)`

在数据库表中为带有 `model_name` 的模型创建一个 [约束](/zh-hans/5.1/ref/models/constraints/)。

### `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 语句之前将其分割成独立的语句。

> **Warning**
>
> 在 PostgreSQL 和 SQLite上，只有在 [非原子性迁移](/zh-hans/5.1/howto/writing-migrations/#non-atomic-migrations) 中的 SQL 中使用 `BEGIN` 或 `COMMIT`，才能避免破坏 Django 的事务状态。

你也可以传递一个字符串或二元元组的列表。后者与 [cursor.execute()](/zh-hans/5.1/topics/db/sql/#executing-custom-sql) 一样，用于传递查询和参数。这三种操作是等价的：

```
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()`](/zh-hans/5.1/topics/db/multi-db/#allow_migrate) 方法，以帮助它们做出路由决策。参见 [提示](/zh-hans/5.1/topics/db/multi-db/#topics-db-multi-db-hints) 了解更多关于数据库提示的细节。

可选的 `elidable` 参数决定了当 [压缩迁移](/zh-hans/5.1/topics/migrations/#migration-squashing) 时，是否会删除（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`](/zh-hans/5.1/ref/schema-editor/#django.db.backends.base.schema.BaseDatabaseSchemaEditor) 的实例。

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

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

可选的 `elidable` 参数决定了当 [压缩迁移](/zh-hans/5.1/topics/migrations/#migration-squashing) 时，是否会删除（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),
    ]
```

这通常是你用来创建 [数据迁移](/zh-hans/5.1/topics/migrations/#data-migrations) 的操作，运行自定义数据更新和更改，以及其他任何你需要访问 ORM 和／或 Python 代码的操作。

就像 [`RunSQL`](#django.db.migrations.operations.RunSQL) 一样，确保如果你在这里改变模式，你要么是在 Django 模型系统的范围外进行（例如触发器），要么你使用 [`SeparateDatabaseAndState`](#django.db.migrations.operations.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` 操作是 [非原子性的迁移](/zh-hans/5.1/howto/writing-migrations/#non-atomic-migrations) 的一部分，那么只有当 `atomic=True` 传递给 `RunPython` 操作时，该操作才会在事务中执行。

> **Warning**
>
> `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`](#django.db.migrations.operations.RunSQL) 的 `state_operations` 参数的通用版本）。当要求它对数据库进行更改时，它将使用 `database_operations` 列表。

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

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

## 操作类别

> **New in Django 5.1**

#### `class OperationCategory`

由 [`makemigrations`](/zh-hans/5.1/ref/django-admin/#django-admin-makemigrations) 命令使用的迁移操作类别，用于显示有意义的符号。

#### `ADDITION`

*符号*: `+`

#### `REMOVAL`

*符号*: `-`

#### `ALTERATION`

*符号*: `~`

#### `PYTHON`

*符号*: `p`

#### `SQL`

*符号*: `s`

#### `MIXED`

*符号*: `?`

## 自己写

操作有一个相对简单的 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

    # This categorizes the operation. The corresponding symbol will be
    # displayed by the makemigrations command.
    category = OperationCategory.ADDITION

    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.
        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)
```

你可以使用这个模板，并在此基础上进行工作，不过我们建议查看 `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` 的实现，这是自动检测代码的一部分，对自定义操作并不重要。

> **Warning**
>
> 由于性能原因，`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
```
