迁移操作¶
迁移文件由一个或多个 Operation
组成,这些对象声明性地记录了迁移应该对您的数据库执行的操作。
Django 还使用这些 Operation
对象来确定您的模型在历史上是什么样的,并计算自上次迁移以来您对模型所做的更改,以便它可以自动编写您的迁移;这就是为什么它们是声明性的,因为这意味着 Django 可以轻松地将它们全部加载到内存中并运行它们,而无需触碰数据库来确定您的项目应该是什么样子。
还有一些更专业的 Operation
对象,用于 数据迁移 和高级手动数据库操作。您也可以编写自己的 Operation
类,如果您想封装您经常进行的自定义更改。
如果您需要一个空迁移文件来将您自己的 Operation
对象写入其中,请使用 python manage.py makemigrations --empty yourappname
,但请注意,手动添加更改架构的操作可能会混淆迁移自动检测器,并导致 makemigrations
的后续运行输出不正确的代码。
所有核心 Django 操作都可以在 django.db.migrations.operations
模块中使用。
有关入门资料,请参阅 迁移主题指南。
架构操作¶
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)
的 2 元组列表。列表中的第一个管理器将在迁移期间成为此模型的默认管理器。
RenameModel
¶
-
class
RenameModel
(old_name, new_name)¶
将模型从旧名称重命名为新名称。
如果您更改了模型的名称以及相当多的字段,您可能需要手动添加此操作;对于自动检测器来说,这看起来像是您删除了一个旧名称的模型并添加了一个新名称的模型,它创建的迁移将丢失旧表中的任何数据。
AlterModelTableComment
¶
-
class
AlterModelTableComment
(name, table_comment)¶
更改模型的表注释(Meta
子类上的 db_table_comment
选项)。
AlterUniqueTogether
¶
-
class
AlterUniqueTogether
(name, unique_together)¶
更改模型的唯一约束集(Meta
子类上的 unique_together
选项)。
AlterIndexTogether
¶
-
class
AlterIndexTogether
(name, index_together)¶
更改模型的自定义索引集(Meta
子类上的 index_together
选项)。
警告
AlterIndexTogether
仅在 Django 4.2 之前的迁移文件中正式支持。出于向后兼容性的原因,它仍然是公共 API 的一部分,并且没有计划弃用或删除它,但它不应用于新的迁移。请改用 AddIndex
和 RemoveIndex
操作。
AlterOrderWithRespectTo
¶
-
class
AlterOrderWithRespectTo
(name, order_with_respect_to)¶
创建或删除 _order
列,该列需要 Meta
子类上的 order_with_respect_to
选项。
AlterModelOptions
¶
-
class
AlterModelOptions
(name, options)¶
存储对各种模型选项(模型的 Meta
上的设置)的更改,例如 permissions
和 verbose_name
。不会影响数据库,但会为 RunPython
实例保留这些更改。 options
应该是一个字典,将选项名称映射到值。
AddField
¶
-
class
AddField
(model_name, name, field, 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
¶
-
class
RemoveField
(model_name, name)¶
从模型中删除字段。
请记住,当反转时,这实际上是在向模型添加字段。如果字段是可为空的,或者它具有可用于填充重新创建的列的默认值,则该操作是可逆的(除了任何数据丢失,这是不可逆的)。如果字段不可为空且没有默认值,则该操作是不可逆的。
PostgreSQL
RemoveField
还会删除与已删除字段相关的任何其他数据库对象(例如视图)。这是因为生成的 DROP COLUMN
语句将包含 CASCADE
子句以确保 表之外的依赖对象也被删除。
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
已设置,则更改其列名)。
RenameIndex
¶
-
class
RenameIndex
(model_name, new_name, old_name=None, old_fields=None)¶
重命名具有 model_name
的模型的数据库表中的索引。只能提供 old_name
和 old_fields
之一。 old_fields
是字符串的可迭代对象,通常对应于 index_together
的字段。
在不支持索引重命名语句的数据库(SQLite 和 MariaDB < 10.5.2)上,该操作将删除并重新创建索引,这可能很昂贵。
特殊操作¶
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 的事务状态。
您还可以传递字符串列表或 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_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()
方法,以帮助它们做出路由决策。有关数据库提示的更多详细信息,请参阅 Hints。
可选的 elidable
参数决定在 压缩迁移 时是否删除(省略)操作。
-
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.apps.registry.Apps
的实例,其中包含与操作在项目历史记录中的位置匹配的历史模型,第二个是 SchemaEditor
的实例。
当取消应用迁移时,会调用 reverse_code
参数。此可调用对象应撤消 code
可调用对象中所做的操作,以便迁移可逆。如果 reverse_code
为 None
(默认值),则 RunPython
操作不可逆。
可选的 hints
参数将作为 **hints
传递给数据库路由器的 allow_migrate()
方法,以帮助它们做出路由决策。有关数据库提示的更多详细信息,请参阅 Hints。
可选的 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
是您函数的第二个参数)。
-
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
的示例,请参阅 将 ManyToManyField 更改为使用 through 模型。
编写您自己的¶
操作具有相对简单的 API,并且它们的设计使您可以轻松地编写自己的操作来补充内置的 Django 操作。 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
中的内置 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() ...
to_state
在 database_backwards 方法中是较旧的状态;也就是说,迁移完成反转后,它将成为当前状态。您可能会在内置操作中看到
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