移行操作
移行ファイルは、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)
モデルの名前を古い名前から新しい名前に変更します。
モデルの名前とそのフィールドのかなりの数を一度に変更する場合は、これを手動で追加する必要がある場合があります。 自動検出器では、これは古い名前のモデルを削除し、別の名前の新しいモデルを追加したように見えます。モデルが作成する移行では、古いテーブルのデータが失われます。
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)
permissions
やverbose_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)
タイプ、 null 、 unique 、 db_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を実行する前に個々のステートメントに分割します。
文字列または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_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()メソッドに渡され、ルーティングの決定を支援します。 データベースヒントの詳細については、ヒントを参照してください。
オプションの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_code
がNone
(デフォルト)の場合、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=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
は関数の2番目の引数)を指定しない限り、デフォルトのデータベースに移動します)。
- static RunPython.noop()
- 指定された方向に操作を実行しないようにする場合は、
RunPython.noop
メソッドをcode
またはreverse_code
に渡します。 これは、操作を元に戻すのに特に役立ちます。
SeparateDatabaseAndState
- class SeparateDatabaseAndState(database_operations=None, state_operations=None)
高度に専門化された操作により、操作のデータベース(スキーマ変更)と状態(自動検出器への電力供給)の側面を組み合わせることができます。
2つの操作リストを受け入れます。 状態を適用するように求められると、state_operations
リストが使用されます(これは RunSQL のstate_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
と履歴モデルの取得に使用されるパターン、およびModelState
とstate_forwards()
の履歴モデルの変更に使用されるパターン。
注意すべきいくつかの事柄:
移行を作成するために、
ProjectState
についてあまり学ぶ必要はありません。 アプリレジストリへのアクセスを許可するapps
プロパティがあることを知っておいてください(get_model
を呼び出すことができます)。database_forwards
とdatabase_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.fields
の Field インスタンスは移行間で再利用されます。 これらのインスタンスの属性は絶対に変更しないでください。 state_forwards()
のフィールドを変更する必要がある場合は、ModelState.fields
から古いインスタンスを削除し、その場所に新しいインスタンスを追加する必要があります。 ModelState.managers
の Manager インスタンスについても同じことが言えます。
例として、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