移行操作—Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/ref/migration-operations
移動先:案内検索

移行操作

移行ファイルは、1つ以上のOperationで構成されます。これらのオブジェクトは、データベースに対して移行が行うべきことを宣言的に記録します。

Djangoはまた、これらのOperationオブジェクトを使用して、モデルが過去にどのように見えたかを計算し、最後の移行以降にモデルに加えた変更を計算して、移行を自動的に書き込むことができるようにします。 Djangoがデータベースに触れることなく、それらすべてをメモリに簡単にロードして実行し、プロジェクトがどのように見えるかを判断できるため、宣言型であるのはそのためです。

データ移行などの高度な手動データベース操作用のより特殊なOperationオブジェクトもあります。 一般的に行うカスタム変更をカプセル化する場合は、独自のOperationクラスを作成することもできます。

独自のOperationオブジェクトを書き込むために空の移行ファイルが必要な場合は、python manage.py makemigrations --empty yourappnameを使用しますが、スキーマ変更操作を手動で追加すると、移行自動検出機能が混乱し、結果としてが実行される可能性があることに注意してください。 ]:djadmin: `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タプルのリストを取ります。 リストの最初のマネージャーは、移行中のこのモデルのデフォルトマネージャーになります。


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)

permissionsverbose_nameなどのその他のモデルオプション(モデルのMetaの設定)への変更を保存します。 データベースには影響しませんが、 RunPython インスタンスが使用するためにこれらの変更を保持します。 optionsは、オプション名を値にマッピングする辞書である必要があります。


AlterModelManagers

class AlterModelManagers(name, managers)

移行中に使用可能なマネージャーを変更します。


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)を示します。 )-通常、移行によってnull不可能なフィールドがテーブルに追加され、既存の行に配置するにはデフォルト値が必要になるためです。 データベースにデフォルトを直接設定する動作には影響しません。Djangoはデータベースのデフォルトを設定することはなく、常にDjangoORMコードに適用します。

警告

古いデータベースでは、デフォルト値でフィールドを追加すると、テーブルが完全に書き換えられる場合があります。 これはnull許容フィールドでも発生し、パフォーマンスに悪影響を与える可能性があります。 これを回避するには、次の手順を実行する必要があります。

  • デフォルト値なしでnull許容フィールドを追加し、:djadmin: `makemigrations` コマンドを実行します。 これにより、AddField操作で移行が生成されます。
  • フィールドにデフォルト値を追加し、:djadmin: `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)を示します。 )-通常、移行によってnull許容フィールドがnull不可能フィールドに変更され、既存の行に配置するにはデフォルト値が必要になるためです。 データベースにデフォルトを直接設定する動作には影響しません。Djangoはデータベースのデフォルトを設定することはなく、常にDjangoORMコードに適用します。

すべてのデータベースですべての変更が可能なわけではないことに注意してください。たとえば、ほとんどのデータベースでは、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のモデルのデータベーステーブルに constraint を作成します。


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では、Djangoのトランザクション状態を壊さないように、非アトミックマイグレーションのSQLでBEGINまたはCOMMITのみを使用してください。


文字列または2タプルのリストを渡すこともできます。 後者は、 cursor.execute()と同じ方法でクエリとパラメーターを渡すために使用されます。 これらの3つの操作は同等です。

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

クエリにリテラルのパーセント記号を含めたい場合、パラメータを渡す場合はそれらを2倍にする必要があります。

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引数は、**hintsとしてデータベースルーターの allow_migrate()メソッドに渡され、ルーティングの決定を支援します。 データベースヒントの詳細については、ヒントを参照してください。

オプションの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)は、2つの引数を受け入れる呼び出し可能なオブジェクトである必要があります。 1つ目はプロジェクト履歴内の操作の場所に一致する履歴モデルを含むdjango.apps.registry.Appsのインスタンスであり、2つ目は SchemaEditor のインスタンスです。

reverse_code引数は、移行を適用解除するときに呼び出されます。 この呼び出し可能オブジェクトは、code呼び出し可能オブジェクトで行われたことを元に戻して、移行を元に戻す必要があります。 reverse_codeNone(デフォルト)の場合、RunPythonの操作は元に戻せません。

オプションのhints引数は、**hintsとしてデータベースルーターの allow_migrate()メソッドに渡され、ルーティングの決定を支援します。 データベースヒントの詳細については、ヒントを参照してください。

オプションの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=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操作が非アトミック移行の一部である場合、atomic=TrueRunPythonに渡された場合にのみ、操作はトランザクションで実行されます。手術。

警告

RunPythonは、モデルの接続を魔法のように変更することはありません。 呼び出すモデルメソッドは、現在のデータベースエイリアス(schema_editor.connection.aliasから利用可能、schema_editorは関数の2番目の引数)を指定しない限り、デフォルトのデータベースに移動します)。


static RunPython.noop()
指定された方向に操作を実行しないようにする場合は、RunPython.noopメソッドをcodeまたはreverse_codeに渡します。 これは、操作を元に戻すのに特に役立ちます。


SeparateDatabaseAndState

class SeparateDatabaseAndState(database_operations=None, state_operations=None)

高度に専門化された操作により、操作のデータベース(スキーマ変更)と状態(自動検出器への電力供給)の側面を組み合わせることができます。

2つの操作リストを受け入れます。 状態を適用するように求められると、state_operationsリストが使用されます(これは RunSQLstate_operations引数の一般化されたバージョンです)。 データベースに変更を適用するように求められると、database_operationsリストが使用されます。

データベースの実際の状態とDjangoの状態のビューが同期しなくなると、移行フレームワークが壊れ、データが失われる可能性があります。 注意を払い、データベースと状態の操作を注意深くチェックすることは価値があります。 :djadmin: `sqlmigrate` および:djadmin:` dbshell` を使用して、データベースの動作を確認できます。 :djadmin: `makemigrations` を使用して、特に--dry-runで、状態の操作を確認できます。

SeparateDatabaseAndStateの使用例については、スルーモデルを使用するためのManyToManyFieldの変更を参照してください。


自分で書く

操作には比較的単純な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)

バージョン3.2の新機能: migration_name_fragmentプロパティが追加されました。


このテンプレートを使用して作業することができますが、django.db.migrations.operationsに組み込まれているDjango操作を確認することをお勧めします。これらは、ProjectStateと履歴モデルの取得に使用されるパターン、およびModelStatestate_forwards()の履歴モデルの変更に使用されるパターン。

注意すべきいくつかの事柄:

  • 移行を作成するために、ProjectStateについてあまり学ぶ必要はありません。 アプリレジストリへのアクセスを許可するappsプロパティがあることを知っておいてください(get_modelを呼び出すことができます)。

  • database_forwardsdatabase_backwardsはどちらも、2つの状態が渡されます。 これらは、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.fieldsField インスタンスは移行間で再利用されます。 これらのインスタンスの属性は絶対に変更しないでください。 state_forwards()のフィールドを変更する必要がある場合は、ModelState.fieldsから古いインスタンスを削除し、その場所に新しいインスタンスを追加する必要があります。 ModelState.managersManager インスタンスについても同じことが言えます。


例として、PostgreSQL拡張機能(PostgreSQLのよりエキサイティングな機能のいくつかを含む)をロードする操作を作成してみましょう。 モデルの状態は変化しないため、1つのコマンドを実行するだけです。

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