迁移操作

迁移文件由一个或多个 Operation s,声明性地记录迁移应该对数据库做什么的对象。

Django也使用这些 Operation 对象计算出模型的历史外观,并计算自上次迁移以来对模型所做的更改,以便它可以自动编写迁移;这就是为什么它们是声明性的,因为这意味着Django可以轻松地将它们全部加载到内存中并在其中运行,而无需触摸数据库即可计算出我们的项目应该是这样的。

还有更专业的 Operation 对象,用于 data migrations 以及高级手动数据库操作。你也可以自己写 Operation 类,如果您想封装通常所做的自定义更改。

如果您需要一个空的迁移文件来编写自己的 Operation 对象,使用 python manage.py makemigrations --empty yourappname ,但请注意,手动添加模式更改操作可能会混淆迁移自动检测程序,并导致运行 makemigrations 输出错误代码。

所有核心的Django操作都可以从 django.db.migrations.operations 模块。

有关介绍性材料,请参见 migrations topic guide .

架构操作

CreateModel

class CreateModel(name, fields, options=None, bases=None, managers=None)[源代码]

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

name 是模型名称,如 models.py 文件。

fields 是两个元组的列表 (field_name, field_instance) . 字段实例应该是未绑定的字段(因此 models.CharField(...) 而不是从另一个模型中获取的字段)。

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

bases 是要从中继承此模型的其他类的可选列表;它可以同时包含类对象和格式为的字符串 "appname.ModelName" 如果您想依赖于另一个模型(因此从历史版本继承)。如果未提供,则默认为从标准继承 models.Model .

managers 获取2个元组的列表 (manager_name, manager_instance) . 列表中的第一个管理器将是迁移期间此模型的默认管理器。

DeleteModel

class DeleteModel(name)[源代码]

从项目历史记录中删除模型,并从数据库中删除其表。

RenameModel

class RenameModel(old_name, new_name)[源代码]

将模型从旧名称重命名为新名称。

如果一次更改模型的名称及其相当多的字段,则可能需要手动添加此项;对于自动检测程序,这看起来就像删除了一个旧名称的模型,并添加了一个新名称不同的模型,而它创建的迁移将丢失旧表中的任何数据。

AlterModelTable

class AlterModelTable(name, table)[源代码]

更改模型的表名 db_table 选择权 Meta 子类)。

AlterModelTableComment

class AlterModelTableComment(name, table_comment)[源代码]

更改模型的表格注释( db_table_comment 选项上的 Meta 子类)。

AlterUniqueTogether

class AlterUniqueTogether(name, unique_together)[源代码]

更改模型的唯一约束集 unique_together 选择权 Meta 子类)。

AlterIndexTogether

class AlterIndexTogether(name, index_together)[源代码]

更改模型的一组自定义索引( index_together 选项上的 Meta 子类)。

警告

AlterIndexTogether 官方仅支持Django 4.2之前的迁移文件。出于向后兼容性的原因,它仍然是公共API的一部分,没有计划弃用或删除它,但不应该将其用于新的迁移。使用 AddIndexRemoveIndex 取而代之的是运营。

AlterOrderWithRespectTo

class AlterOrderWithRespectTo(name, order_with_respect_to)[源代码]

使或删除 _order 列需要用于 order_with_respect_to 选择权 Meta 子类。

AlterModelOptions

class AlterModelOptions(name, options)[源代码]

存储对其他模型选项(模型上的设置)的更改 Meta 喜欢 permissionsverbose_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代码中。

警告

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

  • 添加不带默认值的可空字段并运行 makemigrations 命令。这将生成一个带有 AddField 操作。

  • 并运行默认值以运行该字段 makemigrations 命令。这将生成一个带有 AlterField 操作。

RemoveField

class RemoveField(model_name, name)[源代码]

从模型中删除字段。

请记住,当反转时,这实际上是在向模型中添加字段。如果字段可为Null或具有可用于填充重新创建的列的默认值,则操作是可逆的(除了任何数据丢失,这是不可逆的)。如果字段不可为null且没有默认值,则操作不可逆。

AlterField

class AlterField(model_name, name, field, preserve_default=True)[源代码]

更改字段的定义,包括对其类型的更改, nulluniquedb_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)[源代码]

删除名为的索引 name 从模型中 model_name .

RenameIndex

class RenameIndex(model_name, new_name, old_name=None, old_fields=None)[源代码]

使用重命名模型的数据库表中的索引 model_name 。恰好是其中之一 old_nameold_fields 可以提供。 old_fields 是字符串的可迭代,通常对应于 index_together (Django 5.1之前的选项)。

在不支持索引重命名语句的数据库(SQLite和MariaDB<10.5.2)上,该操作将删除并重新创建索引,这可能是代价高昂的。

AddConstraint

class AddConstraint(model_name, constraint)[源代码]

创建一个 constraint 在模型的数据库表中 model_name .

RemoveConstraint

class RemoveConstraint(model_name, name)[源代码]

删除名为的约束 name 从模型中 model_name .

特种作业

RunSQL

class RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False)[源代码]

允许在数据库上运行任意SQL—对于Django不直接支持的更高级的数据库后端功能非常有用。

sqlreverse_sql 如果提供,则应该是要在数据库上运行的SQL字符串。在大多数数据库后端(除了PostgreSQL之外的所有后端),Django将在执行SQL之前将其拆分为单独的语句。

警告

在PostgreSQL和SQLite上,仅使用 BEGINCOMMIT 在你的SQL中 non-atomic migrations ,以避免破坏Django的事务状态。

还可以传递字符串或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 当迁移未应用时,将执行查询。他们应该撤销 sql 查询。例如,要用删除操作撤消上述插入操作:

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

如果 reverse_sqlNone (默认),即 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 参数将作为 **hintsallow_migrate() 数据库路由器帮助他们做出路由决策的方法。见 提示 有关数据库提示的详细信息。

可选的 elidable 参数确定当 squashing migrations .

RunSQL.noop

通过 RunSQL.noop 属性到 sqlreverse_sql 当您希望操作不在给定的方向上执行任何操作时。这在使操作可逆方面特别有用。

RunPython

class RunPython(code, reverse_code=None, atomic=None, hints=None, elidable=False)[源代码]

在历史上下文中运行自定义python代码。 code (和 reverse_code 如果提供)应该是接受两个参数的可调用对象;第一个参数是 django.apps.registry.Apps 包含与操作在项目历史中的位置匹配的历史模型,第二个是 SchemaEditor .

这个 reverse_code 当取消应用迁移时调用参数。此可调用项应撤消 code 可调用以便迁移是可逆的。如果 reverse_codeNone (默认),即 RunPython 操作是不可逆的。

可选的 hints 参数将作为 **hintsallow_migrate() 数据库路由器的一种方法,帮助它们做出路由决策。见 提示 有关数据库提示的详细信息。

可选的 elidable 参数确定当 squashing migrations .

建议您将代码作为单独的函数写入 Migration 在迁移文件中初始化,并将其传递给 RunPython . 下面是一个使用 RunPythonCountry 型号:

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),
    ]

这通常是您将用来创建的操作 data migrations ,运行自定义数据更新和更改,以及您需要访问的ORM和/或python代码。

很像 RunSQL ,请确保如果在此处更改架构,则在Django模型系统(例如触发器)范围之外执行,或者使用 SeparateDatabaseAndState 要添加将反映您对模型状态所做更改的操作,否则版本控制的ORM和自动检测程序将停止正常工作。

默认情况下, RunPython 将在不支持DDL事务的数据库(例如,MySQL和Oracle)上的事务内部运行其内容。这应该是安全的,但如果您尝试使用 schema_editor 在这些后端提供;在这种情况下,通过 atomic=FalseRunPython 操作。

在支持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 操作是 non-atomic migration ,只有在以下情况下,该操作才会在事务中执行: atomic=True 传递给 RunPython 操作。

警告

RunPython 不会神奇地为您更改模型的连接;您调用的任何模型方法都将转到默认数据库,除非您为它们提供当前的数据库别名(可从 schema_editor.connection.alias 在哪里 schema_editor 是函数的第二个参数)。

static RunPython.noop()[源代码]

通过 RunPython.noop 方法到 codereverse_code 当您希望操作不在给定的方向上执行任何操作时。这在使操作可逆方面特别有用。

SeparateDatabaseAndState

class SeparateDatabaseAndState(database_operations=None, state_operations=None)[源代码]

一种高度专业化的操作,允许您混合和匹配操作的数据库(模式更改)和状态(自动检测器供电)方面。

它接受两个操作列表。当要求应用状态时,它将使用 state_operations 列表(这是 RunSQLstate_operations 参数)。当要求对数据库应用更改时,它将使用 database_operations 名单。

如果数据库的实际状态和Django的状态视图不同步,这可能会破坏迁移框架,甚至导致数据丢失。值得谨慎操作并仔细检查数据库和状态操作。你可以用 sqlmigratedbshell 检查数据库操作。你可以用 makemigrations 尤其是 --dry-run ,以检查状态操作。

例如,使用 SeparateDatabaseAndState更改 ManyToManyField 使用A through 模型 .

写你自己的

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

您可以使用此模板并从中进行工作,不过我们建议您查看 django.db.migrations.operations -它们涵盖了迁移框架的许多半内部方面的示例用法,例如 ProjectState 以及用于获取历史模型的模式,以及 ModelState 以及用来改变历史模型的模式 state_forwards() .

需要注意的一些事项:

  • 你不需要学习太多 ProjectState 编写迁移;只需知道它有一个 apps 允许访问应用程序注册表(然后可以调用该注册表)的属性 get_model 在)。

  • database_forwardsdatabase_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 在数据库中,向后方法是 老年人 状态;也就是说,迁移完成后将成为当前状态的状态。

  • 您可能会看到 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

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