sql >> データベース >  >> RDS >> Database

Djangoの移行を深く掘り下げる

    これは、Django移行シリーズの2番目の記事です:

    • パート1:Djangoの移行:入門書
    • パート2:Djangoの移行を深く掘り下げる(現在の記事)
    • パート3:データ移行
    • ビデオ:Django1.7の移行-入門書

    このシリーズの前回の記事では、Djangoの移行の目的について学びました。移行の作成や適用などの基本的な使用パターンに慣れてきました。次に、移行システムをさらに深く掘り下げて、その基盤となるメカニズムのいくつかを覗いてみましょう。

    この記事の終わりまでに、次のことがわかります。

    • Djangoが移行を追跡する方法
    • 移行が実行するデータベース操作をどのように認識するか
    • 移行間の依存関係の定義方法

    Django移行システムのこの部分に頭を悩ませたら、独自のカスタム移行を作成する準備が整います。中断したところから始めましょう!

    この記事ではbitcoin_trackerを使用しています Django Migrations:APrimerで構築されたDjangoプロジェクト。その記事に目を通すことでそのプロジェクトを再作成するか、ソースコードをダウンロードすることができます:

    ソースコードのダウンロード: この記事で使用する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つの主要なリストが含まれています:

    1. dependencies
    2. 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つの操作クラスを提供しています。

    1. RunSQL データベースでカスタムSQLを実行できます。
    2. RunPython 任意のPythonコードを実行できます。
    3. 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.pydependencies 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)も意味します。 、00020003 、…)は、移行が適用される順序を厳密に表すものではありません。必要に応じて依存関係を追加できるため、すべての移行に番号を付け直すことなく順序を制御できます。



    移行の表示

    通常、移行によって生成される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つのリストでインスタンス化されます:

    1. state_operations プロジェクトの状態にのみ適用される操作が含まれています。
    2. database_operations データベースにのみ適用される操作が含まれています。

    この操作により、データベースにあらゆる種類の変更を加えることができますが、後でプロジェクトの状態がデータベースに適合することを確認するのはユーザーの責任です。 SeparateDatabaseAndStateのユースケースの例 モデルをあるアプリから別のアプリに移動したり、ダウンタイムなしで巨大なデータベースにインデックスを作成したりしています。

    SeparateDatabaseAndState は高度な操作であり、最初の日に移行を行う必要はなく、おそらくまったく必要ありません。 SeparateDatabaseAndState 心臓外科に似ています。それはかなりのリスクを伴い、単に楽しみのために行うことではありませんが、患者を生かし続けるために必要な手順である場合があります。




    結論

    これで、Djangoの移行について詳しく説明しました。おめでとう!あなたはかなり多くの高度なトピックをカバーし、移行の内部で何が起こるかをしっかりと理解しました。

    あなたはそれを学びました:

    • Djangoは、適用された移行をDjango移行テーブルで追跡します。
    • Djangoの移行は、Migrationを含むプレーンなPythonファイルで構成されています クラス。
    • Djangoは、operationsから実行する変更を認識しています。 Migrationのリスト クラス。
    • Djangoは、モデルを移行から構築したプロジェクトの状態と比較します。

    この知識があれば、Djangoの移行に関するシリーズの第3部に取り組む準備が整いました。ここでは、データ移行を使用してデータに1回限りの変更を安全に行う方法を学習します。しばらくお待ちください!

    この記事ではbitcoin_trackerを使用しました Django Migrations:APrimerで構築されたDjangoプロジェクト。その記事に目を通すことでそのプロジェクトを再作成するか、ソースコードをダウンロードすることができます:

    ソースコードのダウンロード: この記事で使用するDjango移行プロジェクトのコードをダウンロードするには、ここをクリックしてください。



    1. SAPLumiraとJDBC-ODBCブリッジ

    2. SQLServerでの常時接続の可用性グループの設定と構成

    3. SQL Server(T-SQL)で「smalldatetime」を「datetime」に変換する例

    4. MySQL>テーブルが存在しません。しかし、それはします(またはそうすべきです)