迁移操作

迁移文件由一个或多个Operation组成,这些对象声明性地记录迁移对数据库应执行的操作。

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

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

If you need an empty migration file to write your own Operation objects into, just use python manage.py makemigrations --empty yourappname, but be aware that manually adding schema-altering operations can confuse the migration autodetector and make resulting runs of makemigrations output incorrect code.

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

有关介绍材料,请参阅migrations topic guide

架构操作

CreateModel

CreateModel(name, fields, options=None, bases=None, managers=None)[source]

在项目历史记录中创建一个新模型,并在数据库中创建相应的表格以匹配它。

name is the model name, as would be written in the models.py file.

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

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

bases is an optional list of other classes to have this model inherit from; it can contain both class objects as well as strings in the format "appname.ModelName" if you want to depend on another model (so you inherit from the historical version). 如果没有提供,则默认从标准models.Model继承。

managers取得(manager_name, manager_instance)的2元组列表。 列表中的第一个管理员将成为迁移过程中此模型的默认管理者。

DeleteModel

DeleteModel(name)[source]

从项目历史记录和数据库中的表中删除模型。

RenameModel

RenameModelold_namenew_name[source]

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

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

AlterModelTable

AlterModelTable(name, table)[source]

更改模型的表名(Meta子类中的db_table选项)。

AlterUniqueTogether

AlterUniqueTogether(name, unique_together)[source]

更改模型的唯一约束集(Meta子类上的unique_together选项)。

AlterIndexTogether

AlterIndexTogether(name, index_together)[source]

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

AlterOrderWithRespectTo

AlterOrderWithRespectTo(name, order_with_respect_to)[source]

创建或删除Meta子类上的order_with_respect_to选项所需的_order列。

AlterModelOptions

AlterModelOptions(name, options)[source]

permissionsverbose_name的其他模型选项(模型的Meta上的设置)更改存储。 不会影响数据库,但可以使用RunPython实例保留这些更改。 options should be a dictionary mapping option names to values.

AlterModelManagers

AlterModelManagers(name, managers)[source]

更改迁移期间可用的管理员。

AddField

AddField(model_name, name, field, preserve_default=True)[source]

添加一个字段到模型。 model_name是模型名称,name是字段名称,field是一个未绑定的Field实例(您将放入字段声明在models.py中 - 例如models.IntegerField(null=True)

preserve_default参数指示该字段的默认值是否为永久性的,并且应该烘焙到项目状态(True)中,或者是临时的,仅用于此迁移(False) - 通常是因为迁移正在向表中添加一个不可为空的字段,并且需要一个默认值来放入现有的行。 它不会影响直接在数据库中设置默认值的行为 - Django从不设置数据库默认值,并始终将它们应用于Django的ORM代码中。

RemoveField

RemoveField(model_name, name)[source]

从模型中删除一个字段。

请记住,相反,这实际上是添加一个字段模型。 该操作是可逆的(除了任何数据丢失,当然这是不可逆的),如果该字段是可空的或者它具有可以用来填充重新创建的列的默认值。 如果该字段不可为空并且没有默认值,则该操作是不可逆的。

AlterField

AlterField(model_name, name, field, preserve_default=True)[source]

改变字段的定义,包括对其类型,nulluniquedb_column和其他字段属性的更改。

preserve_default参数指示该字段的默认值是否为永久性的,并且应该烘焙到项目状态(True)中,或者是暂时的,仅用于此迁移(False) - 通常是因为迁移将可空字段更改为非空字段,并且需要将默认值放入现有行中。 它不会影响直接在数据库中设置默认值的行为 - Django从不设置数据库默认值,并始终将它们应用于Django的ORM代码中。

请注意,并非所有数据库都可以进行所有更改,例如,不能像models.TextField()那样将文本类型字段更改为数字类型字段,如models.IntegerField()在大多数数据库上。

RenameField

RenameFieldmodel_nameold_namenew_name[source] T6>

更改一个字段的名称(除非设置了db_column,它的列名)。

AddIndex

AddIndexmodel_nameindex[source]
Django 1.11新增功能

model_name在模型的数据库表中创建一个索引。 indexIndex类的一个实例。

RemoveIndex

RemoveIndexmodel_namename[source]
Django 1.11新增功能

使用model_name从模型中删除名为name的索引。

特殊操作

RunSQL

RunSQL(sql, reverse_sql=None, state_operations=None, hints=None, elidable=False)[source]

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

如果提供了sqlreverse_sql,则应该是在数据库上运行的SQL字符串。 在大多数数据库后端(除了PostgreSQL之外),Django会在执行之前将SQL分解成单独的语句。 这需要安装sqlparse Python库。

您也可以传递一个字符串或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查询,因此您可以撤消在转发查询中完成的更改:

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

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参数确定当squashing migrations时操作是否会被删除(消除)。

RunSQL。空操作 T0> ¶ T1>

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

RunPython

RunPython tt>(codereverse_code = Noneatomic = Nonehints = Noneelidable = False[source]

在历史上下文中运行自定义Python代码。 code (and reverse_code if supplied) should be callable objects that accept two arguments; the first is an instance of django.apps.registry.Apps containing historical models that match the operation’s place in the project history, and the second is an instance of SchemaEditor.

在不应用迁移时调用reverse_code参数。 这个可调用的函数应该撤消code中可调用的内容,以便迁移是可逆的。

可选的hints参数将作为**hints传递给数据库路由器的allow_migrate()方法,以帮助他们做出路由决策。 有关数据库提示的更多详细信息,请参阅Hints

可选的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代码的操作。

如果你是从南方升级,这基本上是南方模式作为一个操作 - 一个或两个方法的前进和后退,与ORM和架构操作可用。 Most of the time, you should be able to translate the orm.Model or orm["appname", "Model"] references from South directly into apps.get_model("appname", "Model") references here and leave most of the rest of the code unchanged for data migrations. 但是,除非将其他应用程序中的迁移添加到迁移的依赖项,否则apps只会引用当前应用程序中的模型。

就像RunSQL,确保如果你在这里改变模式,你要么在Django模型系统的范围之外(例如触发器),要么使用SeparateDatabaseAndState添加将反映您对模型状态的更改的操作 - 否则,版本化的ORM和自动检测器将停止正常工作。

By default, RunPython will run its contents inside a transaction on databases that do not support DDL transactions (for example, MySQL and Oracle). 这应该是安全的,但如果您尝试使用这些后端提供的schema_editor,可能会导致崩溃;在这种情况下,将atomic=False传递给RunPython操作。

On databases that do support DDL transactions (SQLite and PostgreSQL), RunPython operations do not have any transactions automatically added besides the transactions created for each migration. 因此,例如,在PostgreSQL上,您应该避免在同一迁移中将模式更改和RunPython操作组合在一起,否则您可能会遇到像 OperationalError: 不能 改变 “MYTABLE” 因为 具有 有待 触发 事件.

如果您有不同的数据库,并且不确定它是否支持DDL事务,请检查django.db.connection.features.can_rollback_ddl属性。

If the RunPython operation is part of a non-atomic migration, the operation will only be executed in a transaction if atomic=True is passed to the RunPython operation.

警告

RunPython does not magically alter the connection of the models for you; any model methods you call will go to the default database unless you give them the current database alias (available from schema_editor.connection.alias, where schema_editor is the second argument to your function).

静态的 RunPython。noop()[source]

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

SeparateDatabaseAndState

SeparateDatabaseAndStatedatabase_operations = Nonestate_operations = None[source]

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

它接受两个操作列表,当被问及应用状态时将使用状态列表,而当询问应用更改时,数据库将使用数据库列表。 除非你确定你知道你在做什么,否则不要使用这个操作。

编写你自己的

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

尽管我们建议您在django.db.migrations.operations中查看内置的Django操作,但您可以使用此模板并从中进行工作 - 它们很容易阅读并涵盖很多示例像ProjectState这样的迁移框架的半内部方面的使用,以及用于获取历史模型的模式,以及ModelState以及state_forwards()

有些事情要注意:

  • 您不需要学习太多关于ProjectState的内容,只需编写简单的迁移;只知道它有一个apps属性,可以访问应用程序注册表(然后可以调用get_model)。

  • database_forwardsdatabase_backwards都会传递给它们两个状态;这些只是代表state_forwards方法应用的区别,但是出于方便和速度的原因给了你。

  • If you want to work with model classes or model instances from the from_state argument in database_forwards() or database_backwards(), you must render model states using the clear_delayed_apps_cache() method to make related models available:

    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()
        ...
    
    Django 1.11新增功能:

    这个要求和clear_delayed_apps_cache()方法是新的。

  • to_state in the database_backwards method is the older state; that is, the one that will be the current state once the migration has finished reversing.

  • 您可能会在内置操作中看到references_model的实现;这是自动检测代码的一部分,对于自定义操作无关紧要。

警告

出于性能方面的考虑,ModelState.fields中的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