これは、Django移行シリーズの2番目の記事です:
- パート1:Djangoの移行:入門書
- パート2:Djangoの移行を深く掘り下げる(現在の記事)
- パート3:データ移行
- ビデオ:Django1.7の移行-入門書
このシリーズの前回の記事では、Djangoの移行の目的について学びました。移行の作成や適用などの基本的な使用パターンに慣れてきました。次に、移行システムをさらに深く掘り下げて、その基盤となるメカニズムのいくつかを覗いてみましょう。
この記事の終わりまでに、次のことがわかります。
- Djangoが移行を追跡する方法
- 移行が実行するデータベース操作をどのように認識するか
- 移行間の依存関係の定義方法
Django移行システムのこの部分に頭を悩ませたら、独自のカスタム移行を作成する準備が整います。中断したところから始めましょう!
この記事ではbitcoin_tracker
を使用しています Django Migrations:APrimerで構築されたDjangoプロジェクト。その記事に目を通すことでそのプロジェクトを再作成するか、ソースコードをダウンロードすることができます:
Djangoがどの移行を適用するかを知る方法
シリーズの前の記事の最後のステップを要約してみましょう。移行を作成し、python manage.py migrate
を使用して利用可能なすべての移行を適用しました 。そのコマンドが正常に実行された場合、データベーステーブルはモデルの定義と一致します。
そのコマンドを再度実行するとどうなりますか?試してみましょう:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
何も起こらなかった!移行がデータベースに適用されると、Djangoはこの移行をその特定のデータベースに再度適用しません。移行が1回だけ適用されるようにするには、適用された移行を追跡する必要があります。
Djangoはdjango_migrations
というデータベーステーブルを使用します 。 Djangoは、最初に移行を適用するときに、データベースにこのテーブルを自動的に作成します。適用または偽造された移行ごとに、新しい行がテーブルに挿入されます。
たとえば、このテーブルがbitcoin_tracker
でどのように表示されるかを次に示します。 プロジェクト:
ID | アプリ | 名前 | 適用 |
---|---|---|---|
1 | contenttypes | 0001_initial | 2019-02-05 20:23:21.461496 |
2 | auth | 0001_initial | 2019-02-05 20:23:21.489948 |
3 | admin | 0001_initial | 2019-02-05 20:23:21.508742 |
4 | admin | 0002_logentry_remove... | 2019-02-05 20:23:21.531390 |
5 | admin | 0003_logentry_add_ac... | 2019-02-05 20:23:21.564834 |
6 | contenttypes | 0002_remove_content_... | 2019-02-05 20:23:21.597186 |
7 | auth | 0002_alter_permissio... | 2019-02-05 20:23:21.608705 |
8 | auth | 0003_alter_user_emai... | 2019-02-05 20:23:21.628441 |
9 | auth | 0004_alter_user_user... | 2019-02-05 20:23:21.646824 |
10 | auth | 0005_alter_user_last... | 2019-02-05 20:23:21.661182 |
11 | auth | 0006_require_content... | 2019-02-05 20:23:21.663664 |
12 | auth | 0007_alter_validator... | 2019-02-05 20:23:21.679482 |
13 | auth | 0008_alter_user_user... | 2019-02-05 20:23:21.699201 |
14 | auth | 0009_alter_user_last... | 2019-02-05 20:23:21.718652 |
15 | historical_data | 0001_initial | 2019-02-05 20:23:21.726000 |
16 | sessions | 0001_initial | 2019-02-05 20:23:21.734611 |
19 | historical_data | 0002_switch_to_decimals | 2019-02-05 20:30:11.337894 |
ご覧のとおり、適用された移行ごとにエントリがあります。この表には、historical_data
からの移行が含まれているだけではありません。 アプリだけでなく、インストールされている他のすべてのアプリからの移行もあります。
次回の移行が実行されると、Djangoはデータベーステーブルにリストされている移行をスキップします。つまり、すでに適用されている移行のファイルを手動で変更した場合でも、データベースにそのエントリがすでに存在する限り、Djangoはこれらの変更を無視します。
テーブルから対応する行を削除することで、Djangoをだまして移行を再実行させることもできますが、これはめったに良い考えではなく、移行システムが壊れたままになる可能性があります。
移行ファイル
python manage.py makemigrations <appname>
を実行するとどうなりますか ? Djangoは、アプリのモデルに加えられた変更を探します<appname>
。追加されたモデルのように、何かが見つかった場合は、migrations
に移行ファイルを作成します。 サブディレクトリ。この移行ファイルには、データベーススキーマをモデル定義と同期させるための操作のリストが含まれています。
注: アプリはINSTALLED_APPS
にリストされている必要があります 設定であり、migrations
が含まれている必要があります __init__.py
のあるディレクトリ ファイル。そうしないと、Djangoは移行を作成しません。
migrations
startapp
を使用して新しいアプリを作成すると、ディレクトリが自動的に作成されます 管理コマンドですが、アプリを手動で作成するときに忘れがちです。
移行ファイルは単なるPythonなので、historical_prices
の最初の移行ファイルを見てみましょう。 アプリ。これはhistorical_prices/migrations/0001_initial.py
にあります。 。次のようになります:
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.CreateModel(
name='PriceHistory',
fields=[
('id', models.AutoField(
verbose_name='ID',
serialize=False,
primary_key=True,
auto_created=True)),
('date', models.DateTimeField(auto_now_add=True)),
('price', models.DecimalField(decimal_places=2, max_digits=5)),
('volume', models.PositiveIntegerField()),
('total_btc', models.PositiveIntegerField()),
],
options={
},
bases=(models.Model,),
),
]
ご覧のとおり、Migration
という単一のクラスが含まれています。 django.db.migrations.Migration
から継承します 。これは、移行を適用するように要求したときに、移行フレームワークが検索して実行するクラスです。
migrations
クラスには2つの主要なリストが含まれています:
dependencies
operations
移行操作
operations
を見てみましょう 最初にリストします。このテーブルには、移行の一部として実行される操作が含まれています。操作は、クラスdjango.db.migrations.operations.base.Operation
のサブクラスです。 。 Djangoに組み込まれている一般的な操作は次のとおりです。
操作クラス | 説明 |
---|---|
CreateModel | 新しいモデルと対応するデータベーステーブルを作成します |
DeleteModel | モデルを削除し、そのデータベーステーブルを削除します |
RenameModel | モデルの名前を変更し、データベーステーブルの名前を変更します |
AlterModelTable | モデルのデータベーステーブルの名前を変更します |
AlterUniqueTogether | モデルの一意の制約を変更します |
AlterIndexTogether | モデルのインデックスを変更します |
AlterOrderWithRespectTo | _order を作成または削除します モデルの列 |
AlterModelOptions | データベースに影響を与えることなく、さまざまなモデルオプションを変更します |
AlterModelManagers | 移行中に使用可能なマネージャーを変更します |
AddField | データベース内のモデルと対応する列にフィールドを追加します |
RemoveField | モデルからフィールドを削除し、対応する列をデータベースから削除します |
AlterField | フィールドの定義を変更し、必要に応じてそのデータベース列を変更します |
RenameField | フィールドの名前を変更し、必要に応じてそのデータベース列の名前も変更します |
AddIndex | モデルのデータベーステーブルにインデックスを作成します |
RemoveIndex | モデルのデータベーステーブルからインデックスを削除します |
データベースで実行されるアクションではなく、モデル定義に加えられた変更に基づいて操作に名前が付けられていることに注意してください。移行を適用する場合、各操作は特定のデータベースに必要なSQLステートメントを生成する責任があります。たとえば、CreateModel
CREATE TABLE
を生成します SQLステートメント。
すぐに使用できる移行では、Djangoがサポートするすべての標準データベースがサポートされます。したがって、ここにリストされている操作に固執すれば、基礎となるSQLを気にすることなく、必要なモデルに多かれ少なかれ変更を加えることができます。これですべて完了です。
注: 場合によっては、Djangoが変更を正しく検出しないことがあります。モデルの名前を変更してそのフィールドのいくつかを変更すると、Djangoはこれを新しいモデルと間違える可能性があります。
RenameModel
の代わりに およびいくつかのAlterField
操作では、DeleteModel
が作成されます およびCreateModel
手術。モデルのデータベーステーブルの名前を変更する代わりに、モデルを削除して新しい名前で新しいテーブルを作成し、すべてのデータを効果的に削除します!
本番データで実行する前に、生成された移行を確認し、データベースのコピーでテストすることを習慣にしてください。
Djangoは、高度なユースケース向けにさらに3つの操作クラスを提供しています。
-
RunSQL
データベースでカスタムSQLを実行できます。 -
RunPython
任意のPythonコードを実行できます。 -
SeparateDatabaseAndState
高度な用途に特化した操作です。
これらの操作を使用すると、基本的にデータベースに必要な変更を加えることができます。ただし、これらの操作は、makemigrations
で自動的に作成された移行では見つかりません。 管理コマンド。
Django 2.0以降、django.contrib.postgres.operations
で利用可能なPostgreSQL固有の操作もいくつかあります。 さまざまなPostgreSQL拡張機能のインストールに使用できます:
-
BtreeGinExtension
-
BtreeGistExtension
-
CITextExtension
-
CryptoExtension
-
HStoreExtension
-
TrigramExtension
-
UnaccentExtension
これらの操作のいずれかを含む移行には、スーパーユーザー権限を持つデータベースユーザーが必要であることに注意してください。
最後になりましたが、独自の操作クラスを作成することもできます。それを調べたい場合は、カスタム移行操作の作成に関するDjangoのドキュメントをご覧ください。
移行の依存関係
dependencies
移行クラスのリストには、この移行を適用する前に適用する必要のある移行が含まれています。
0001_initial.py
内 上で見た移行では、事前に何も適用する必要がないため、依存関係はありません。 historical_prices
の2番目の移行を見てみましょう アプリ。ファイル0002_switch_to_decimals.py
、dependencies
Migration
の属性 エントリがあります:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='pricehistory',
name='volume',
field=models.DecimalField(decimal_places=3, max_digits=7),
),
]
上記の依存関係は、移行0001_initial
アプリのhistorical_data
最初に実行する必要があります。移行0001_initial
なので、それは理にかなっています 移行0002_switch_to_decimals
が実行するフィールドを含むテーブルを作成します 変更したい。
移行は、次のように、別のアプリからの移行に依存することもあります。
class Migration(migrations.Migration):
...
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
]
これは通常、モデルに別のアプリのモデルを指す外部キーがある場合に必要です。
または、移行が前に実行されるように強制することもできます。 属性run_before
を使用した別の移行 :
class Migration(migrations.Migration):
...
run_before = [
('third_party_app', '0001_initial'),
]
依存関係を組み合わせて、複数の依存関係を持つこともできます。この機能は、さまざまなアプリのモデルに依存する外部キーに対応できるため、多くの柔軟性を提供します。
移行間の依存関係を明示的に定義するオプションは、移行の番号付け(通常は0001
)も意味します。 、0002
、0003
、…)は、移行が適用される順序を厳密に表すものではありません。必要に応じて依存関係を追加できるため、すべての移行に番号を付け直すことなく順序を制御できます。
移行の表示
通常、移行によって生成されるSQLについて心配する必要はありません。ただし、生成されたSQLが理にかなっていることを再確認したい場合、またはそれがどのように見えるかを知りたい場合は、Djangoがsqlmigrate
でカバーします。 管理コマンド:
$ python manage.py sqlmigrate historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
CREATE TABLE "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
COMMIT;
これを行うと、settings.py
のデータベースに基づいて、指定された移行によって生成される基になるSQLクエリが一覧表示されます。 ファイル。パラメータ--backwards
を渡すと 、DjangoはSQLを生成して、移行を適用解除します:
$ python manage.py sqlmigrate --backwards historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
DROP TABLE "historical_data_pricehistory";
COMMIT;
sqlmigrate
の出力が表示されたら もう少し複雑な移行の場合は、このSQLをすべて手作業で作成する必要がないことを理解してください。
Djangoがモデルの変更を検出する方法
移行ファイルがどのように見えるか、およびそのoperations
のリストがどのようになっているのかを確認しました クラスは、データベースに対して実行される変更を定義します。しかし、Djangoはどの操作を移行ファイルに入れるべきかをどの程度正確に知っていますか? Djangoがモデルをデータベーススキーマと比較することを期待するかもしれませんが、そうではありません。
makemigrations
を実行する場合 、Djangoはしません データベースを検査します。また、モデルファイルを以前のバージョンと比較することもありません。代わりに、Djangoは適用されたすべての移行を実行し、モデルがどのように見えるかを示すプロジェクト状態を構築します。次に、このプロジェクトの状態が現在のモデル定義と比較され、操作のリストが作成されます。これを適用すると、プロジェクトの状態がモデル定義で最新になります。
Djangoでチェスをする
モデルはチェス盤のように考えることができます。Djangoはチェスのグランドマスターであり、自分と対戦するのを見守っています。しかし、グランドマスターはあなたのすべての動きを監視しているわけではありません。グランドマスターは、makemigrations
と叫んだときにのみボードを確認します 。
可能な動きは限られているため(そしてグランドマスターはグランドマスターです)、彼女は最後にボードを見てから起こった動きを思い付くことができます。彼女はいくつかのメモを取り、makemigrations
と叫ぶまでプレイさせてくれます もう一度。
次回ボードを見るとき、グランドマスターは前回のチェス盤の様子を覚えていませんが、前の動きのメモを調べて、チェス盤がどのように見えたかのメンタルモデルを構築できます。
>
さて、あなたがmigrate
と叫ぶとき 、グランドマスターは、記録されたすべての動きを別のチェス盤で再生し、スプレッドシートに彼女の記録のどれがすでに適用されているかを記録します。この2番目のチェス盤はデータベースであり、スプレッドシートはdjango_migrations
です。 テーブル。
このアナロジーは、Djangoの移行のいくつかの動作をうまく示しているため、非常に適切です。
-
Djangoの移行は効率的になるように努めています: グランドマスターがあなたが行った移動の数が最も少ないと想定するのと同じように、Djangoは最も効率的な移行を作成しようとします。
A
という名前のフィールドを追加した場合 モデルに変更し、名前をB
に変更します 、次にmakemigrations
を実行します 、次にDjangoは新しい移行を作成して、B
という名前のフィールドを追加します 。 -
Djangoの移行には制限があります: グランドマスターにチェス盤を見てもらう前に多くの動きをすると、各ピースの正確な動きをたどることができない場合があります。同様に、一度に多くの変更を加えると、Djangoが正しい移行を思い付かない可能性があります。
-
Djangoの移行では、ルールに従ってプレイする必要があります: ボードからランダムなピースを取り出したり、メモをいじったりするなど、予期しないことをした場合、グランドマスターは最初は気付かないかもしれませんが、遅かれ早かれ、手を挙げて続行を拒否します。
django_migrations
をいじったときにも同じことが起こります モデルのデータベーステーブルを削除するなどして、移行以外でデータベーススキーマをテーブル化または変更します。
SeparateDatabaseAndState
Djangoが構築するプロジェクトの状態がわかったので、次は操作SeparateDatabaseAndState
を詳しく見てみましょう。 。この操作は、その名前が示すとおりに実行できます。つまり、プロジェクトの状態(Djangoが構築するメンタルモデル)をデータベースから分離できます。
SeparateDatabaseAndState
操作の2つのリストでインスタンス化されます:
-
state_operations
プロジェクトの状態にのみ適用される操作が含まれています。 -
database_operations
データベースにのみ適用される操作が含まれています。
この操作により、データベースにあらゆる種類の変更を加えることができますが、後でプロジェクトの状態がデータベースに適合することを確認するのはユーザーの責任です。 SeparateDatabaseAndState
のユースケースの例 モデルをあるアプリから別のアプリに移動したり、ダウンタイムなしで巨大なデータベースにインデックスを作成したりしています。
SeparateDatabaseAndState
は高度な操作であり、最初の日に移行を行う必要はなく、おそらくまったく必要ありません。 SeparateDatabaseAndState
心臓外科に似ています。それはかなりのリスクを伴い、単に楽しみのために行うことではありませんが、患者を生かし続けるために必要な手順である場合があります。
結論
これで、Djangoの移行について詳しく説明しました。おめでとう!あなたはかなり多くの高度なトピックをカバーし、移行の内部で何が起こるかをしっかりと理解しました。
あなたはそれを学びました:
- Djangoは、適用された移行をDjango移行テーブルで追跡します。
- Djangoの移行は、
Migration
を含むプレーンなPythonファイルで構成されています クラス。 - Djangoは、
operations
から実行する変更を認識しています。Migration
のリスト クラス。 - Djangoは、モデルを移行から構築したプロジェクトの状態と比較します。
この知識があれば、Djangoの移行に関するシリーズの第3部に取り組む準備が整いました。ここでは、データ移行を使用してデータに1回限りの変更を安全に行う方法を学習します。しばらくお待ちください!
この記事ではbitcoin_tracker
を使用しました Django Migrations:APrimerで構築されたDjangoプロジェクト。その記事に目を通すことでそのプロジェクトを再作成するか、ソースコードをダウンロードすることができます: