移行
移行は、モデルに加えた変更(フィールドの追加、モデルの削除など)をデータベーススキーマに伝達するDjangoの方法です。 これらはほとんど自動で設計されていますが、移行を行うタイミング、実行するタイミング、および発生する可能性のある一般的な問題を知る必要があります。
コマンド
移行とDjangoのデータベーススキーマの処理を操作するために使用するコマンドがいくつかあります。
- :djadmin: `migrate` 。移行の適用と適用解除を担当します。
- :djadmin: `makemigrations` 。モデルに加えた変更に基づいて、新しい移行を作成します。
- :djadmin: `sqlmigrate` 。移行用のSQLステートメントを表示します。
- :djadmin: `showmigrations` 。プロジェクトの移行とそのステータスが一覧表示されます。
移行は、データベーススキーマのバージョン管理システムと考える必要があります。 makemigrations
は、モデルの変更を個々の移行ファイルにパッケージ化する役割を果たします(コミットと同様)。migrate
は、それらをデータベースに適用する責任があります。
各アプリの移行ファイルは、そのアプリ内の「移行」ディレクトリにあり、そのコードベースにコミットされ、その一部として配布されるように設計されています。 開発マシンで一度作成してから、同僚のマシン、ステージングマシン、そして最終的には本番マシンで同じ移行を実行する必要があります。
移行は同じデータセットで同じ方法で実行され、一貫した結果が生成されます。つまり、開発とステージングで表示されるのは、同じ状況下で、本番環境で発生することとまったく同じです。
Djangoは、モデルまたはフィールドへの変更(データベースに影響を与えないオプションも含む)に対して移行を行います。フィールドを正しく再構築できる唯一の方法は、履歴にすべての変更を加えることであり、これらのオプションが必要になる場合があります。後でいくつかのデータ移行(たとえば、カスタムバリデーターを設定した場合)。
バックエンドサポート
移行は、Djangoに付属するすべてのバックエンド、およびスキーマ変更をサポートするようにプログラムされている場合はサードパーティのバックエンドでサポートされます( SchemaEditor クラスを介して実行)。
ただし、スキーマの移行に関しては、一部のデータベースは他のデータベースよりも機能が優れています。 注意事項のいくつかを以下に示します。
PostgreSQL
PostgreSQLは、スキーマのサポートに関して、ここにあるすべてのデータベースの中で最も機能があります。
唯一の注意点は、PostgreSQL 11より前では、デフォルト値で列を追加すると、テーブルのサイズに比例した時間、テーブルが完全に書き換えられることです。 このため、null=True
を使用して新しい列を常に作成することをお勧めします。これにより、列がすぐに追加されます。
MySQL
MySQLには、スキーマ変更操作に関するトランザクションのサポートがありません。つまり、移行が適用されない場合、再試行するには手動で変更の選択を解除する必要があります(以前のポイントにロールバックすることはできません)。
さらに、MySQLはほぼすべてのスキーマ操作でテーブルを完全に書き換え、通常、列を追加または削除するためにテーブルの行数に比例した時間がかかります。 低速のハードウェアでは、これは100万行あたり1分よりも悪い場合があります。数百万行のテーブルに数列を追加すると、サイトが10分以上ロックされる可能性があります。
最後に、MySQLには、列、テーブル、およびインデックスの名前の長さに対する比較的小さな制限と、インデックスがカバーするすべての列の合計サイズに対する制限があります。 これは、他のバックエンドで可能なインデックスがMySQLで作成されないことを意味します。
SQLite
SQLiteにはスキーマ変更のサポートがほとんど組み込まれていないため、Djangoは次の方法でそれをエミュレートしようとします。
- 新しいスキーマで新しいテーブルを作成する
- 全体にデータをコピーする
- 古いテーブルを削除する
- 元の名前と一致するように新しいテーブルの名前を変更する
このプロセスは一般的にはうまく機能しますが、時間がかかり、場合によってはバグが発生する可能性があります。 リスクとその制限を十分に認識していない限り、本番環境でSQLiteを実行して移行することはお勧めしません。 Djangoに付属するサポートは、開発者がローカルマシンでSQLiteを使用して、完全なデータベースを必要とせずに、それほど複雑でないDjangoプロジェクトを開発できるように設計されています。
ワークフロー
Djangoはあなたのために移行を作成することができます。 モデルに変更を加えます(たとえば、フィールドを追加してモデルを削除します)。次に、:djadmin: `makemigrations` :を実行します。
$ python manage.py makemigrations
Migrations for 'books':
books/migrations/0003_auto.py:
- Alter field author on book
モデルがスキャンされ、移行ファイルに現在含まれているバージョンと比較されてから、新しい移行セットが書き出されます。 出力を読んで、makemigrations
が変更したと見なすものを確認してください。これは完全ではなく、複雑な変更の場合は、期待したものを検出できない可能性があります。
新しい移行ファイルを入手したら、それらをデータベースに適用して、期待どおりに機能することを確認する必要があります。
$ python manage.py migrate
Operations to perform:
Apply all migrations: books
Running migrations:
Rendering model states... DONE
Applying books.0003_auto... OK
移行が適用されたら、移行をコミットすると、モデルがバージョン管理システムに1回のコミットとして変更されます。そうすれば、他の開発者(または本番サーバー)がコードをチェックアウトすると、モデルに対する両方の変更が取得されます。同時にそれに伴う移行。
生成された名前ではなく、意味のある名前を移行に付けたい場合は、makemigrations --name
オプションを使用できます。
$ python manage.py makemigrations --name changed_my_model your_app_label
バージョン管理
移行はバージョン管理に保存されるため、自分と別の開発者の両方が同時に同じアプリへの移行をコミットし、同じ数の2つの移行が発生する状況に遭遇することがあります。
心配しないでください。数字は開発者の参照用にあります。Djangoは、移行ごとに異なる名前が付けられていることを気にしています。 移行では、ファイル内で依存する他の移行(同じアプリでの以前の移行を含む)を指定するため、同じアプリで注文されていない新しい移行が2つあるかどうかを検出できます。
これが発生すると、Djangoはプロンプトを表示し、いくつかのオプションを提供します。 十分に安全であると判断した場合は、2つの移行を自動的に線形化することを提案します。 そうでない場合は、自分で移行を変更する必要があります。心配しないでください。これは難しくありません。詳細については、以下の移行ファイルを参照してください。
トランザクション
DDLトランザクションをサポートするデータベース(SQLiteおよびPostgreSQL)では、すべての移行操作はデフォルトで単一のトランザクション内で実行されます。 対照的に、データベースがDDLトランザクションをサポートしていない場合(例: MySQL、Oracle)の場合、すべての操作はトランザクションなしで実行されます。
atomic
属性をFalse
に設定することにより、トランザクションで移行が実行されないようにすることができます。 例えば:
from django.db import migrations
class Migration(migrations.Migration):
atomic = False
atomic()を使用するか、atomic=True
を RunPython に渡すことにより、トランザクション内で移行の一部を実行することもできます。 詳細については、非アトミックマイグレーションを参照してください。
依存関係
移行はアプリごとですが、モデルによって示されるテーブルと関係は複雑すぎて、一度に1つのアプリ用に作成することはできません。 何か他のものを実行する必要がある移行を行う場合(たとえば、books
アプリのForeignKey
をauthors
アプリに追加する場合)、結果の移行には依存関係が含まれますauthors
での移行時。
つまり、移行を実行すると、authors
移行が最初に実行され、ForeignKey
が参照するテーブルが作成され、次にForeignKey
列を作成する移行が実行されて作成されます。制約。 これが行われなかった場合、移行は、参照しているテーブルが存在しない状態でForeignKey
列を作成しようとし、データベースはエラーをスローします。
この依存関係の動作は、単一のアプリに制限するほとんどの移行操作に影響します。 単一のアプリ(makemigrations
またはmigrate
のいずれか)に制限することは、最善の努力の約束であり、保証ではありません。 依存関係を正しく取得するために使用する必要がある他のアプリはすべてそうなります。
移行のないアプリは、移行のあるアプリとの関係(ForeignKey
、ManyToManyField
など)があってはなりません。 動作する場合もありますが、サポートされていません。
移行ファイル
移行は、ここでは「移行ファイル」と呼ばれるディスク上の形式で保存されます。 これらのファイルは、実際には、宣言型で記述された、合意されたオブジェクトレイアウトを持つ通常のPythonファイルです。
基本的な移行ファイルは次のようになります。
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.DeleteModel('Tribble'),
migrations.AddField('Author', 'rating', models.IntegerField(default=0)),
]
Djangoが移行ファイルを(Pythonモジュールとして)ロードするときに探すのは、Migration
と呼ばれるdjango.db.migrations.Migration
のサブクラスです。 次に、このオブジェクトの4つの属性を検査しますが、ほとんどの場合、そのうちの2つだけが使用されます。
dependencies
、これが依存する移行のリスト。operations
、この移行の機能を定義するOperation
クラスのリスト。
操作が鍵です。 これらは、どのスキーマ変更を行う必要があるかをDjangoに指示する一連の宣言型命令です。 Djangoはそれらをスキャンし、すべてのアプリに対するすべてのスキーマ変更のメモリ内表現を構築し、これを使用してスキーマ変更を行うSQLを生成します。
このメモリ内構造は、モデルと移行の現在の状態との違いを理解するためにも使用されます。 Djangoは、メモリ内のモデルセットに対してすべての変更を順番に実行し、最後にmakemigrations
を実行したときのモデルの状態を判断します。 次に、これらのモデルを使用して、models.py
ファイル内のモデルと比較し、変更内容を特定します。
移行ファイルを手動で編集する必要があることはめったにありませんが、必要に応じて手動で書き込むことは完全に可能です。 より複雑な操作のいくつかは自動検出できず、手書きの移行を介してのみ利用できるため、必要に応じて編集することを恐れないでください。
カスタムフィールド
TypeError
を上げずに、すでに移行されたカスタムフィールドの位置引数の数を変更することはできません。 古い移行では、変更された__init__
メソッドが古い署名で呼び出されます。 したがって、新しい引数が必要な場合は、キーワード引数を作成し、コンストラクターにassert 'argument_name' in kwargs
のようなものを追加してください。
モデルマネージャー
オプションで、マネージャーを移行にシリアル化し、 RunPython 操作で使用できるようにすることができます。 これは、マネージャークラスでuse_in_migrations
属性を定義することによって行われます。
class MyManager(models.Manager):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
from_queryset()関数を使用してマネージャークラスを動的に生成する場合は、生成されたクラスから継承してインポート可能にする必要があります。
class MyManager(MyBaseManager.from_queryset(CustomQuerySet)):
use_in_migrations = True
class MyModel(models.Model):
objects = MyManager()
移行における履歴モデルに関する注記を参照して、関連する影響を確認してください。
初期移行
- Migration.initial
アプリの「初期移行」は、そのアプリのテーブルの最初のバージョンを作成する移行です。 通常、アプリには1つの初期移行がありますが、複雑なモデルの相互依存関係の場合は、2つ以上になることがあります。
最初の移行は、移行クラスのinitial = True
クラス属性でマークされます。 initial
クラス属性が見つからない場合、それがアプリでの最初の移行である場合、移行は「初期」と見なされます(つまり、 同じアプリ内の他の移行に依存していない場合)。
migrate --fake-initial
オプションを使用すると、これらの初期移行は特別に処理されます。 1つ以上のテーブルを作成する最初の移行(CreateModel
操作)の場合、Djangoはそれらのテーブルがすべてデータベースにすでに存在することを確認し、存在する場合は移行を偽適用します。 同様に、1つ以上のフィールドを追加する最初の移行(AddField
操作)の場合、Djangoはそれぞれの列がすべてデータベースにすでに存在することを確認し、存在する場合は偽の移行を適用します。 --fake-initial
がない場合、最初の移行は他の移行と同じように扱われます。
履歴の一貫性
前に説明したように、2つの開発ブランチが結合されている場合は、移行を手動で線形化する必要がある場合があります。 移行の依存関係を編集しているときに、移行が適用されたが、一部の依存関係が適用されていない、一貫性のない履歴状態を誤って作成する可能性があります。 これは、依存関係が正しくないことを強く示しているため、Djangoは、修正されるまで移行の実行または新しい移行の作成を拒否します。 複数のデータベースを使用する場合は、データベースルーターの allow_migrate()メソッドを使用して、:djadmin: `makemigrations` が一貫した履歴をチェックするデータベースを制御できます。
アプリへの移行の追加
新しいアプリは移行を受け入れるように事前構成されているため、変更を加えたら:djadmin: `makemigrations` を実行して移行を追加できます。
アプリにすでにモデルとデータベーステーブルがあり、まだ移行がない場合(たとえば、以前のDjangoバージョンに対して作成した場合)、次のコマンドを実行して、移行を使用するようにアプリを変換する必要があります。
$ python manage.py makemigrations your_app_label
これにより、アプリの新しい初期移行が行われます。 ここでpython manage.py migrate --fake-initial
を実行すると、Djangoは、作成するテーブルがすでに存在することを示す初期移行とがあることを検出し、移行をすでに適用済みとしてマークします。 (migrate --fake-initial
フラグがないと、作成するテーブルが既に存在するため、コマンドはエラーになります。)
これは、次の2つの場合にのみ機能することに注意してください。
- テーブルを作成してから、モデルを変更していません。 移行を機能させるには、最初の移行最初を行ってから変更を加える必要があります。これは、Djangoがデータベースではなく移行ファイルと変更を比較するためです。
- データベースを手動で編集していません-Djangoは、データベースがモデルと一致していないことを検出できません。移行でこれらのテーブルを変更しようとすると、エラーが発生するだけです。
移行を元に戻す
移行は、:djadmin: `migrate` を使用して、前の移行の番号を渡すことで元に戻すことができます。 たとえば、移行を逆にするにはbooks.0003
:
アプリに適用されたすべての移行を元に戻す場合は、zero
という名前を使用します。
移行に不可逆的な操作が含まれている場合、移行は不可逆的です。 このような移行を元に戻そうとすると、IrreversibleError
が発生します。
歴史的モデル
移行を実行すると、Djangoは移行ファイルに保存されているモデルの履歴バージョンから機能します。 RunPython 操作を使用してPythonコードを作成する場合、またはデータベースルーターにallow_migrate
メソッドがある場合は、これらの履歴モデルバージョンをインポートするのではなく、使用する必要があります直接。
警告
履歴モデルを使用せずにモデルを直接インポートする場合、移行は最初は機能する可能性がありますが、将来、古い移行を再実行しようとすると失敗します(通常、新しいインストールをセットアップして実行する場合)。データベースをセットアップするためのすべての移行を通じて)。
これは、履歴モデルの問題がすぐには明らかにならない可能性があることを意味します。 この種の障害が発生した場合は、直接インポートしてそれらの変更をコミットするのではなく、履歴モデルを使用するように移行を編集しても問題ありません。
任意のPythonコードをシリアル化することは不可能であるため、これらの履歴モデルには、定義したカスタムメソッドがありません。 ただし、これらのフィールド、関係、マネージャー(use_in_migrations = True
のオプションに限定)およびMeta
オプション(バージョンもあるため、現在のものとは異なる場合があります)があります。
警告
つまり、移行時にオブジェクトにアクセスするときに、オブジェクトに対してカスタムsave()
メソッドが呼び出されることはなく、カスタムコンストラクターやインスタンスメソッドもありません。 適切に計画してください!
upload_to
やlimit_choices_to
などのフィールドオプションでの関数への参照、およびuse_in_migrations = True
を持つマネージャーを使用したモデルマネージャー宣言は移行時にシリアル化されるため、関数とクラスを維持する必要があります。それらを参照する移行がある限り。 カスタムモデルフィールドも、移行によって直接インポートされるため、保持する必要があります。
さらに、モデルの具象基本クラスはポインターとして格納されるため、それらへの参照を含む移行がある限り、基本クラスを常に保持する必要があります。 プラス面として、これらの基本クラスのメソッドとマネージャーは通常どおり継承されるため、どうしてもこれらにアクセスする必要がある場合は、それらをスーパークラスに移動することを選択できます。
古い参照を削除するには、移行をスカッシュするか、参照が少ない場合は、それらを移行ファイルにコピーします。
モデルフィールドを削除する際の考慮事項
前のセクションで説明した「履歴関数への参照」の考慮事項と同様に、プロジェクトまたはサードパーティアプリからカスタムモデルフィールドを削除すると、古い移行で参照されている場合に問題が発生します。
この状況を支援するために、Djangoはシステムチェックフレームワークを使用してモデルフィールドの非推奨を支援するいくつかのモデルフィールド属性を提供します。
次のように、system_check_deprecated_details
属性をモデルフィールドに追加します。
class IPAddressField(Field):
system_check_deprecated_details = {
'msg': (
'IPAddressField has been deprecated. Support for it (except '
'in historical migrations) will be removed in Django 1.9.'
),
'hint': 'Use GenericIPAddressField instead.', # optional
'id': 'fields.W900', # pick a unique ID for your field.
}
選択した非推奨期間(Django自体のフィールドの2つまたは3つの機能リリース)の後、system_check_deprecated_details
属性をsystem_check_removed_details
に変更し、次のように辞書を更新します。
class IPAddressField(Field):
system_check_removed_details = {
'msg': (
'IPAddressField has been removed except for support in '
'historical migrations.'
),
'hint': 'Use GenericIPAddressField instead.',
'id': 'fields.E900', # pick a unique ID for your field.
}
__init__()
、deconstruct()
、get_internal_type()
など、データベース移行で動作するために必要なフィールドのメソッドを保持する必要があります。 フィールドを参照する移行が存在する限り、このスタブフィールドを保持します。 たとえば、移行を破棄して古い移行を削除すると、フィールドを完全に削除できるようになります。
データ移行
データベーススキーマを変更するだけでなく、必要に応じてスキーマと組み合わせて、移行を使用してデータベース自体のデータを変更することもできます。
データを変更する移行は通常、「データ移行」と呼ばれます。 それらは、スキーマの移行と並行して、個別の移行として作成するのが最適です。
Djangoは、スキーマ移行の場合のようにデータ移行を自動的に生成することはできませんが、データ移行を作成するのはそれほど難しくありません。 Djangoの移行ファイルは操作で構成されており、データ移行に使用する主な操作は RunPython です。
まず、作業可能な空の移行ファイルを作成します(Djangoはファイルを適切な場所に配置し、名前を提案し、依存関係を追加します)。
python manage.py makemigrations --empty yourappname
次に、ファイルを開きます。 次のようになります。
# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
]
これで、新しい関数を作成して RunPython に使用させるだけです。 RunPython は、2つの引数を取る引数としてcallableを想定しています。1つ目はアプリレジストリで、すべてのモデルの履歴バージョンが読み込まれ、履歴のどこに移行されたかが一致します。 2番目は SchemaEditor で、これを使用してデータベーススキーマの変更を手動で行うことができます(ただし、これを行うと、移行自動検出器が混乱する可能性があることに注意してください)。
新しいname
フィールドにfirst_name
とlast_name
の値を組み合わせた値を入力する移行を記述しましょう(私たちは気づき、誰もが最初と最後を持っているわけではないことに気づきました名前)。 必要なのは、履歴モデルを使用して行を反復処理することだけです。
from django.db import migrations
def combine_names(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
Person = apps.get_model('yourappname', 'Person')
for person in Person.objects.all():
person.name = '%s %s' % (person.first_name, person.last_name)
person.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(combine_names),
]
それが完了すると、通常どおりpython manage.py migrate
を実行でき、データ移行は他の移行と並行して実行されます。
RunPython に2番目の呼び出し可能オブジェクトを渡して、逆方向に移行するときに実行するロジックを実行できます。 このcallableを省略した場合、逆方向に移行すると例外が発生します。
他のアプリからモデルにアクセスする
移行が行われているアプリ以外のアプリのモデルを使用するRunPython
関数を作成する場合、移行のdependencies
属性には、関連する各アプリの最新の移行を含める必要があります。 apps.get_model()
を使用してRunPython
関数でモデルを取得しようとすると、LookupError: No installed app with label 'myappname'
のようなエラーが発生します。
次の例では、app1
に移行があり、app2
のモデルを使用する必要があります。 move_m1
の詳細については、両方のアプリからモデルにアクセスする必要があるという事実以外は関係ありません。 したがって、app2
の最後の移行を指定する依存関係を追加しました。
class Migration(migrations.Migration):
dependencies = [
('app1', '0001_initial'),
# added dependency to enable using models from app2 in move_m1
('app2', '0004_foobar'),
]
operations = [
migrations.RunPython(move_m1),
]
移行を押しつぶす
移行の数を気にせずに、自由に移行することをお勧めします。 移行コードは、一度に数百を処理するように最適化されており、速度を大幅に低下させることはありません。 ただし、最終的には、数百回の移行から数回の移行に戻したいと思うでしょう。そこで、押しつぶしが必要になります。
押しつぶしとは、多くの移行の既存のセットを、同じ変更を表す1つ(場合によってはいくつか)の移行に減らす行為です。
Djangoは、既存のすべての移行を取得し、それらのOperation
を抽出してすべてを順番に配置し、オプティマイザーを実行してリストの長さを短縮しようとすることでこれを行います。たとえば、次のことを認識しています。 CreateModel と DeleteModel は互いに打ち消し合い、 AddField を CreateModel にロールインできることを認識しています。
操作シーケンスが可能な限り削減されたら、可能な量は、モデルがどれだけ密接に絡み合っているか、および RunSQL または RunPython 操作があるかどうかによって異なります(これはできません)。 elidable
としてマークされていない限り、最適化されます)-Djangoはそれを新しい移行ファイルのセットに書き戻します。
これらのファイルは、以前に破棄された移行を置き換えることを示すためにマークされているため、古い移行ファイルと共存でき、Djangoは履歴のどこにいるかに応じてインテリジェントにそれらを切り替えます。 押しつぶした一連の移行の途中である場合は、最後に到達するまでそれらを使用し続けてから押しつぶされた履歴に切り替えますが、新しいインストールでは新しい押しつぶされた移行を使用し、古いものをすべてスキップします。
これにより、現在本番環境にあり、まだ完全に最新ではないシステムを潰すことができ、混乱することはありません。 推奨されるプロセスは、古いファイルを押しつぶし、コミットしてリリースし、すべてのシステムが新しいリリースでアップグレードされるまで待つことです(または、サードパーティプロジェクトの場合は、ユーザーがリリースをスキップせずに順番にアップグレードするようにします)。次に、古いファイルを削除し、コミットして2番目のリリースを実行します。
これらすべてをサポートするコマンドは、:djadmin: `squashmigrations` です。スカッシュするアプリのラベルと移行名を渡すと、次のように機能します。
$ ./manage.py squashmigrations myapp 0004
Will squash the following migrations:
- 0001_initial
- 0002_some_change
- 0003_another_change
- 0004_undo_something
Do you wish to proceed? [yN] y
Optimizing...
Optimized from 12 operations to 7 operations.
Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_somthing.py
You should commit this migration but leave the old ones in place;
the new migration will be used for new installs. Once you are sure
all instances of the codebase have applied the migrations you squashed,
you can delete them.
自動生成された移行を使用するのではなく、押しつぶされた移行の名前を設定する場合は、squashmigrations --squashed-name
オプションを使用します。
Djangoのモデルの相互依存性は非常に複雑になる可能性があり、押しつぶすと移行が実行されない可能性があることに注意してください。 最適化されていない(この場合、--no-optimize
で再試行できますが、問題を報告する必要があります)、またはCircularDependencyError
で、手動で解決できます。
CircularDependencyError
を手動で解決するには、循環依存ループ内のForeignKeysの1つを別の移行に分割し、それを使用して他のアプリへの依存関係を移動します。 よくわからない場合は、モデルから新しい移行を作成するように求められたときに、:djadmin: `makemigrations` が問題にどのように対処するかを確認してください。 Djangoの将来のリリースでは、:djadmin: `squashmigrations` が更新され、これらのエラー自体の解決が試みられます。
移行を破棄したら、それを置き換える移行と一緒にコミットし、この変更をアプリケーションの実行中のすべてのインスタンスに配布し、migrate
を実行して変更をデータベースに保存するようにします。
次に、次の方法で、押しつぶされた移行を通常の移行に移行する必要があります。
- 置き換えるすべての移行ファイルを削除します。
- 削除された移行に依存するすべての移行を、代わりに押しつぶされた移行に依存するように更新します。
- 押しつぶされた移行の
Migration
クラスのreplaces
属性を削除します(これは、Djangoが押しつぶされた移行であることを示す方法です)。
ノート
移行を押しつぶした後は、通常の移行に完全に移行するまで、押しつぶした移行を再度押しつぶさないでください。
値のシリアル化
移行は、モデルの古い定義を含むPythonファイルです。したがって、それらを書き込むには、Djangoはモデルの現在の状態を取得し、ファイルにシリアル化する必要があります。
Djangoはほとんどのものをシリアル化できますが、有効なPython表現にシリアル化できないものもあります-値をコードに戻す方法に関するPython標準はありません(repr()
は基本的な場合にのみ機能します)値であり、インポートパスを指定しません)。
Djangoは以下をシリアル化できます:
int
、float
、bool
、str
、bytes
、None
、NoneType
list
、set
、tuple
、dict
、range
。datetime.date
、datetime.time
、およびdatetime.datetime
インスタンス(タイムゾーン対応のものを含む)decimal.Decimal
インスタンスenum.Enum
インスタンスuuid.UUID
インスタンスfunctools.partial()
およびfunctools.partialmethod
インスタンスで、シリアル化可能なfunc
、args
、およびkeywords
の値があります。pathlib
の純粋で具体的なパスオブジェクト。 具体的なパスは、同等の純粋なパスに変換されます。pathlib.PosixPath
からpathlib.PurePosixPath
。os.PathLike
インスタンス、例:os.DirEntry
は、os.fspath()
を使用してstr
またはbytes
に変換されます。LazyObject
シリアル化可能な値をラップするインスタンス。- 列挙型(例:
TextChoices
またはIntegerChoices
)インスタンス。 - Djangoフィールド
- 関数またはメソッドの参照(例:
datetime.datetime.today
)(モジュールの最上位スコープに含まれている必要があります) - クラス本体内から使用されるバインドされていないメソッド
- すべてのクラス参照(モジュールの最上位スコープ内にある必要があります)
- カスタム
deconstruct()
メソッドを使用するもの(以下を参照)
バージョン3.2で変更: pathlib
およびos.PathLike
インスタンスからの純粋および具象パスオブジェクトのシリアル化サポートが追加されました。
Djangoはシリアル化できません:
- ネストされたクラス
- 任意のクラスインスタンス(例:
MyClass(4.3, 5.7)
) - ラムダ
カスタムシリアライザー
カスタムシリアライザーを作成することにより、他のタイプをシリアル化できます。 たとえば、DjangoがデフォルトでDecimal
をシリアル化していない場合は、次のようにすることができます。
from decimal import Decimal
from django.db.migrations.serializer import BaseSerializer
from django.db.migrations.writer import MigrationWriter
class DecimalSerializer(BaseSerializer):
def serialize(self):
return repr(self.value), {'from decimal import Decimal'}
MigrationWriter.register_serializer(Decimal, DecimalSerializer)
MigrationWriter.register_serializer()
の最初の引数は、シリアライザーを使用する必要がある型または型の反復可能です。
シリアライザーのserialize()
メソッドは、移行での値の表示方法の文字列と、移行で必要なインポートのセットを返す必要があります。
deconstruct()メソッドの追加
クラスにdeconstruct()
メソッドを与えることで、Djangoに独自のカスタムクラスインスタンスをシリアル化させることができます。 引数をとらず、次の3つのタプルを返す必要があります(path, args, kwargs)
。
path
は、クラスへのPythonパスであり、クラス名が最後の部分として含まれている必要があります(たとえば、myapp.custom_things.MyClass
)。 クラスがモジュールのトップレベルで利用できない場合、そのクラスはシリアル化できません。args
は、クラスの__init__
メソッドに渡す位置引数のリストである必要があります。 このリストのすべては、それ自体がシリアル化可能である必要があります。kwargs
は、クラスの__init__
メソッドに渡すキーワード引数の辞書である必要があります。 すべての値はそれ自体がシリアル化可能である必要があります。
Djangoは、Djangoフィールドへの参照を書き出すのと同じように、指定された引数を使用してクラスのインスタンス化として値を書き出します。
:djadmin: `makemigrations` が実行されるたびに新しい移行が作成されないようにするには、装飾されたクラスに__eq__()
メソッドも追加する必要があります。 この関数は、状態間の変化を検出するためにDjangoの移行フレームワークによって呼び出されます。
クラスのコンストラクターへのすべての引数自体がシリアライズ可能である限り、django.utils.deconstruct
の@deconstructible
クラスデコレーターを使用してdeconstruct()
メソッドを追加できます。
from django.utils.deconstruct import deconstructible
@deconstructible
class MyCustomClass:
def __init__(self, foo=1):
self.foo = foo
...
def __eq__(self, other):
return self.foo == other.foo
デコレータは、コンストラクタに向かう途中で引数をキャプチャして保持するロジックを追加し、deconstruct()が呼び出されたときにそれらの引数を正確に返します。
複数のDjangoバージョンをサポート
モデルを使用するサードパーティアプリのメンテナーである場合は、複数のDjangoバージョンをサポートする移行を出荷する必要がある場合があります。 この場合、常に:djadmin: `makemigrations` をサポートしたい最低のDjangoバージョンで実行する必要があります。
移行システムは、他のDjangoと同じポリシーに従って下位互換性を維持するため、DjangoXYで生成された移行ファイルはDjangoX.Y +1で変更せずに実行する必要があります。 ただし、移行システムは上位互換性を約束していません。 新しい機能が追加される可能性があり、新しいバージョンのDjangoで生成された移行ファイルが古いバージョンで機能しない可能性があります。
も参照してください
- 移行操作リファレンス
- スキーマ操作API、特殊操作、および独自の操作の記述について説明します。
- ライティングマイグレーションの「ハウツー」
- 発生する可能性のあるさまざまなシナリオのデータベース移行を構造化および作成する方法について説明します。