複数のデータベース
このトピックガイドでは、複数のデータベースと対話するためのDjangoのサポートについて説明します。 Djangoの残りのドキュメントのほとんどは、単一のデータベースを操作していることを前提としています。 複数のデータベースを操作する場合は、いくつかの追加手順を実行する必要があります。
データベースの定義
Djangoで複数のデータベースを使用するための最初のステップは、使用するデータベースサーバーについてDjangoに通知することです。 これは、:setting: `DATABASES` 設定を使用して行われます。 この設定は、Django全体で特定のデータベースを参照する方法であるデータベースエイリアスを、その特定の接続の設定のディクショナリにマップします。 内部辞書の設定については、:setting: `DATABASES` のドキュメントで詳しく説明されています。
データベースには、選択した任意のエイリアスを含めることができます。 ただし、エイリアスdefault
には特別な意味があります。 Djangoは、他のデータベースが選択されていない場合、エイリアスがdefault
のデータベースを使用します。
以下は、2つのデータベース(デフォルトのPostgreSQLデータベースとusers
と呼ばれるMySQLデータベース)を定義するsettings.py
スニペットの例です。
default
データベースの概念がプロジェクトのコンテキストで意味をなさない場合は、使用するデータベースを常に指定するように注意する必要があります。 Djangoではdefault
データベースエントリを定義する必要がありますが、パラメータディクショナリを使用しない場合は空白のままにすることができます。 これを行うには、使用しているコントリビュートアプリやサードパーティアプリのモデルを含む、すべてのアプリのモデルに対して:setting: `DATABASE_ROUTERS` を設定して、クエリがにルーティングされないようにする必要があります。デフォルトのデータベース。 以下は、2つのデフォルト以外のデータベースを定義するsettings.py
スニペットの例であり、default
エントリは意図的に空のままになっています。
:setting: `DATABASES` 設定で定義していないデータベースにアクセスしようとすると、Djangoはdjango.utils.connection.ConnectionDoesNotExist
例外を発生させます。
データベースの同期
:djadmin: `migrate` 管理コマンドは、一度に1つのデータベースで動作します。 デフォルトでは、default
データベースで動作しますが、--database
オプションを指定することで、別のデータベースを同期するように指示できます。 したがって、上記の最初の例ですべてのモデルをすべてのデータベースに同期するには、次のコマンドを呼び出す必要があります。
すべてのアプリケーションを特定のデータベースに同期させたくない場合は、特定のモデルの可用性を制限するポリシーを実装するデータベースルーターを定義できます。
上記の2番目の例のように、default
データベースを空のままにした場合は、:djadmin: `migrate` を実行するたびにデータベース名を指定する必要があります。 データベース名を省略すると、エラーが発生します。 2番目の例の場合:
他の管理コマンドの使用
データベースと対話する他のほとんどのdjango-admin
コマンドは、:djadmin: `migrate` と同じように動作します。これらは、--database
を使用して、一度に1つのデータベースでのみ動作します。 ]使用するデータベースを制御します。
このルールの例外は、:djadmin: `makemigrations` コマンドです。 データベース内の移行履歴を検証して、新しい移行を作成する前に、既存の移行ファイルの問題(編集によって発生する可能性があります)を検出します。 デフォルトでは、default
データベースのみをチェックしますが、ルーターの allow_migrate()メソッドがインストールされている場合はそれを参照します。
自動データベースルーティング
複数のデータベースを使用する最も簡単な方法は、データベースルーティングスキームを設定することです。 デフォルトのルーティングスキームにより、オブジェクトは元のデータベースに「固定」されたままになります(つまり、foo
データベースから取得されたオブジェクトは同じデータベースに保存されます)。 デフォルトのルーティングスキームでは、データベースが指定されていない場合、すべてのクエリがdefault
データベースにフォールバックします。
デフォルトのルーティングスキームをアクティブにするために何もする必要はありません-それはすべてのDjangoプロジェクトで「箱から出して」提供されます。 ただし、より興味深いデータベース割り当て動作を実装する場合は、独自のデータベースルーターを定義してインストールできます。
データベースルーター
データベースルーターは、最大4つのメソッドを提供するクラスです。
- db_for_read(model, **hints)
タイプ
model
のオブジェクトの読み取り操作に使用する必要があるデータベースを提案します。データベース操作でデータベースの選択に役立つ可能性のある追加情報を提供できる場合は、
hints
ディクショナリで提供されます。 有効なヒントの詳細は、以下に記載されています。提案がない場合は
None
を返します。
- db_for_write(model, **hints)
モデルタイプのオブジェクトの書き込みに使用するデータベースを提案します。
データベース操作でデータベースの選択に役立つ可能性のある追加情報を提供できる場合は、
hints
ディクショナリで提供されます。 有効なヒントの詳細は、以下に記載されています。提案がない場合は
None
を返します。
- allow_relation(obj1, obj2, **hints)
obj1
とobj2
の間の関係を許可する必要がある場合は、True
を返し、関係を防止する必要がある場合はFalse
を返し、ルーターは意見がありません。 これは純粋に検証操作であり、外部キーと、2つのオブジェクト間で関係を許可するかどうかを決定するための多対多の操作で使用されます。ルーターに意見がない場合(つまり すべてのルーターは
None
)を返し、同じデータベース内のリレーションのみが許可されます。
- allow_migrate(db, app_label, model_name=None, **hints)
移行操作がエイリアス
db
のデータベースで実行できるかどうかを確認します。 操作を実行する必要がある場合はTrue
を返し、実行しない場合はFalse
を返し、ルーターが判断を下さない場合はNone
を返します。app_label
位置引数は、移行されるアプリケーションのラベルです。model_name
は、ほとんどの移行操作によって、移行されるモデルのmodel._meta.model_name
(モデル__name__
の小文字バージョン)の値に設定されます。 ヒントを使用して提供されない限り、 RunPython および RunSQL 操作の値はNone
です。hints
は、ルーターに追加情報を伝達するために特定の操作で使用されます。model_name
が設定されている場合、hints
には通常、キー'model'
の下にモデルクラスが含まれます。 履歴モデルである可能性があるため、カスタム属性、メソッド、またはマネージャーがないことに注意してください。_meta
のみに依存する必要があります。この方法は、特定のデータベースでのモデルの可用性を判断するためにも使用できます。
:djadmin: `makemigrations` は常にモデル変更の移行を作成しますが、
allow_migrate()
がFalse
を返す場合、model_name
の移行操作はすべてサイレントにスキップされますdb
で:djadmin: `migrate` を実行している場合。 すでに移行されているモデルのallow_migrate()
の動作を変更すると、外部キーの破損、余分なテーブル、またはテーブルの欠落が発生する可能性があります。 :djadmin: `makemigrations` が移行履歴を確認するとき、アプリの移行が許可されていないデータベースをスキップします。
ルーターは、すべてのこれらのメソッドを提供する必要はありません。1つ以上のメソッドを省略できます。 いずれかの方法を省略した場合、Djangoは関連するチェックを実行するときにそのルーターをスキップします。
ヒント
データベースルーターが受信したヒントを使用して、特定の要求を受信するデータベースを決定できます。
現在、提供されるヒントはinstance
のみです。これは、進行中の読み取りまたは書き込み操作に関連するオブジェクトインスタンスです。 これは、保存されているインスタンスの場合もあれば、多対多の関係で追加されているインスタンスの場合もあります。 場合によっては、インスタンスのヒントがまったく提供されません。 ルータはインスタンスヒントの存在を確認し、そのヒントを使用してルーティング動作を変更する必要があるかどうかを判断します。
ルーターの使用
データベースルーターは、:setting: `DATABASE_ROUTERS` 設定を使用してインストールされます。 この設定は、クラス名のリストを定義し、それぞれがマスタールーター(django.db.router
)によって使用されるルーターを指定します。
マスタールーターは、データベースの使用量を割り当てるためにDjangoのデータベース操作によって使用されます。 クエリが使用するデータベースを知る必要があるときはいつでも、マスタールーターを呼び出して、モデルとヒント(利用可能な場合)を提供します。 次に、Djangoは、データベースの提案が見つかるまで、各ルーターを順番に試行します。 提案が見つからない場合は、ヒントインスタンスの現在の instance._state.db を試行します。 ヒントインスタンスが提供されなかった場合、または instance._state.db がNone
の場合、マスタールーターはdefault
データベースを割り当てます。
例
例の目的のみ!
この例は、ルーターインフラストラクチャを使用してデータベースの使用状況を変更する方法のデモンストレーションを目的としています。 ルーターの使用方法を示すために、いくつかの複雑な問題を意図的に無視しています。
myapp
のモデルのいずれかに、other
データベース外のモデルとの関係が含まれている場合、この例は機能しません。 データベース間の関係は、Djangoが現在処理できない参照整合性の問題をもたらします。
説明されているプライマリ/レプリカ(一部のデータベースではマスター/スレーブと呼ばれます)構成にも欠陥があります。レプリケーションラグ(つまり、書き込みがに伝播するのに時間がかかるために導入されたクエリの不整合)を処理するためのソリューションを提供しません。レプリカ)。 また、トランザクションとデータベース使用率戦略との相互作用も考慮されていません。
それで、これは実際にはどういう意味ですか? 別のサンプル構成を考えてみましょう。 これにはいくつかのデータベースがあります。1つはauth
アプリケーション用で、他のすべてのアプリは2つのリードレプリカでプライマリ/レプリカセットアップを使用します。 これらのデータベースを指定する設定は次のとおりです。
次に、ルーティングを処理する必要があります。 まず、auth
およびcontenttypes
アプリのクエリをauth_db
に送信することを認識しているルーターが必要です(auth
モデルはContentType
にリンクされています] 、したがって、それらは同じデータベースに保存する必要があります):
また、他のすべてのアプリをプライマリ/レプリカ構成に送信し、読み取り元のレプリカをランダムに選択するルーターも必要です。
最後に、設定ファイルに以下を追加します(ルーターが定義されているモジュールへの実際のPythonパスをpath.to.
に置き換えます)。
ルーターが処理される順序は重要です。 ルーターは、:setting: `DATABASE_ROUTERS` 設定にリストされている順序で照会されます。 この例では、AuthRouter
はPrimaryReplicaRouter
の前に処理されるため、auth
のモデルに関する決定は他の決定が行われる前に処理されます。 :setting: `DATABASE_ROUTERS` 設定で2つのルーターが逆の順序でリストされている場合、PrimaryReplicaRouter.allow_migrate()
が最初に処理されます。 PrimaryReplicaRouter実装のキャッチオールの性質は、すべてのモデルがすべてのデータベースで利用可能であることを意味します。
このセットアップがインストールされ、データベースの同期に従ってすべてのデータベースが移行されたら、Djangoコードを実行してみましょう。
この例では、auth
アプリのモデルとの相互作用を処理するルーターと、他のすべてのアプリとの相互作用を処理する他のルーターを定義しました。 default
データベースを空のままにし、特に指定されていないすべてのアプリを処理するキャッチオールデータベースルーターを定義したくない場合、ルーターは:setting: `ですべてのアプリの名前を処理する必要があります。移行する前にINSTALLED_APPS` 。 1つのデータベースにまとめる必要があるcontribアプリについては、 contribappsの動作を参照してください。
データベースを手動で選択する
Djangoは、コード内のデータベースの使用を完全に制御できるAPIも提供します。 手動で指定されたデータベース割り当ては、ルーターによって割り当てられたデータベースよりも優先されます。
QuerySetのデータベースを手動で選択する
QuerySet
「チェーン」の任意の時点でQuerySet
のデータベースを選択できます。 QuerySet
でusing()
を呼び出して、指定されたデータベースを使用する別のQuerySet
を取得します。
using()
は、クエリを実行するデータベースのエイリアスという1つの引数を取ります。 例えば:
save()のデータベースを選択しています
using
キーワードをModel.save()
に使用して、データを保存するデータベースを指定します。
たとえば、オブジェクトをlegacy_users
データベースに保存するには、次を使用します。
using
を指定しない場合、save()
メソッドはルーターによって割り当てられたデフォルトのデータベースに保存されます。
あるデータベースから別のデータベースへのオブジェクトの移動
インスタンスを1つのデータベースに保存した場合、インスタンスを新しいデータベースに移行する方法としてsave(using=...)
を使用したくなるかもしれません。 ただし、適切な手順を実行しないと、予期しない結果が生じる可能性があります。
次の例を考えてみましょう。
ステートメント1では、新しいPerson
オブジェクトがfirst
データベースに保存されます。 現時点では、p
には主キーがないため、DjangoはSQL INSERT
ステートメントを発行します。 これにより主キーが作成され、Djangoはその主キーをp
に割り当てます。
ステートメント2で保存が行われると、p
にはすでに主キー値があり、Djangoは新しいデータベースでその主キーを使用しようとします。 主キー値がsecond
データベースで使用されていない場合、問題は発生しません。オブジェクトは新しいデータベースにコピーされます。
ただし、p
の主キーがsecond
データベースですでに使用されている場合、p
が保存しました。
これは2つの方法で回避できます。 まず、インスタンスの主キーをクリアできます。 オブジェクトに主キーがない場合、Djangoはそれを新しいオブジェクトとして扱い、second
データベース上のデータの損失を回避します。
2番目のオプションは、force_insert
オプションをsave()
に使用して、DjangoがSQL INSERT
を実行するようにすることです。
これにより、Fred
という名前の人が両方のデータベースで同じ主キーを持つようになります。 second
データベースに保存しようとしたときにその主キーがすでに使用されている場合、エラーが発生します。
削除するデータベースの選択
デフォルトでは、既存のオブジェクトを削除するための呼び出しは、最初にオブジェクトを取得するために使用されたのと同じデータベースで実行されます。
モデルの削除元のデータベースを指定するには、using
キーワード引数をModel.delete()
メソッドに渡します。 この引数は、save()
のusing
キーワード引数と同じように機能します。
たとえば、ユーザーをlegacy_users
データベースからnew_users
データベースに移行する場合は、次のコマンドを使用できます。
複数のデータベースでマネージャーを使用する
マネージャーでdb_manager()
メソッドを使用して、マネージャーにデフォルト以外のデータベースへのアクセスを許可します。
たとえば、データベースにアクセスするカスタムマネージャメソッドUser.objects.create_user()
があるとします。 create_user()
はQuerySet
メソッドではなくマネージャーメソッドであるため、User.objects.using('new_users').create_user()
を実行することはできません。 (create_user()
メソッドは、マネージャーから派生したQuerySet
オブジェクトではなく、マネージャーであるUser.objects
でのみ使用できます。)解決策は、db_manager()
を使用することです。このような:
db_manager()
は、指定したデータベースにバインドされているマネージャーのコピーを返します。
複数のデータベースでget_queryset()を使用する
マネージャーでget_queryset()
をオーバーライドする場合は、必ず親でメソッドを呼び出すか(super()
を使用)、で_db
属性を適切に処理してください。 manager(使用するデータベースの名前を含む文字列)。
たとえば、get_queryset
メソッドからカスタムQuerySet
クラスを返す場合は、次のようにします。
Djangoの管理インターフェースで複数のデータベースを公開する
Djangoの管理者は、複数のデータベースを明示的にサポートしていません。 ルーターチェーンで指定されたもの以外のデータベース上のモデルに管理インターフェイスを提供する場合は、コンテンツに特定のデータベースを使用するように管理者に指示するカスタム ModelAdmin クラスを作成する必要があります。 。
ModelAdmin
オブジェクトには、複数データベースをサポートするためのカスタマイズが必要な5つのメソッドがあります。
ここで提供される実装は、特定のタイプのすべてのオブジェクトが特定のデータベースに格納されるマルチデータベース戦略を実装します(たとえば、すべてのUser
オブジェクトはother
データベースにあります)。 複数のデータベースの使用がより複雑な場合は、ModelAdmin
にその戦略を反映させる必要があります。
InlineModelAdmin オブジェクトも同様の方法で処理できます。 3つのカスタマイズされた方法が必要です。
モデル管理者定義を作成したら、それらを任意のAdmin
インスタンスに登録できます。
この例では、2つの管理サイトを設定します。 最初のサイトでは、Author
およびPublisher
オブジェクトが公開されています。 Publisher
オブジェクトには、その出版社によって出版された本を示す表形式のインラインがあります。 2番目のサイトは、インラインなしで発行者のみを公開します。
複数のデータベースで生カーソルを使用する
複数のデータベースを使用している場合は、django.db.connections
を使用して、特定のデータベースの接続(およびカーソル)を取得できます。 django.db.connections
は辞書のようなオブジェクトで、エイリアスを使用して特定の接続を取得できます。
複数のデータベースの制限
データベース間の関係
Djangoは現在、複数のデータベースにまたがる外部キーまたは多対多の関係をサポートしていません。 ルーターを使用してモデルを異なるデータベースに分割した場合、それらのモデルによって定義された外部キーと多対多の関係は、単一のデータベースの内部にある必要があります。
これは、参照整合性のためです。 2つのオブジェクト間の関係を維持するために、Djangoは関連するオブジェクトの主キーが有効であることを知る必要があります。 主キーが別のデータベースに保存されている場合、主キーの有効性を簡単に評価することはできません。
InnoDBでPostgres、Oracle、またはMySQLを使用している場合、これはデータベースの整合性レベルで適用されます。データベースレベルのキー制約により、検証できない関係が作成されなくなります。
ただし、MyISAMテーブルでSQLiteまたはMySQLを使用している場合、強制的な参照整合性はありません。 その結果、クロスデータベースの外部キーを「偽造」できる可能性があります。 ただし、この構成はDjangoによって公式にサポートされていません。
contribアプリの動作
いくつかの投稿アプリにはモデルが含まれており、一部のアプリは他のアプリに依存しています。 データベース間の関係は不可能であるため、これらのモデルをデータベース間で分割する方法にいくつかの制限が生じます。
contenttypes.ContentType
、sessions.Session
、sites.Site
のそれぞれは、適切なルーターがあれば、任意のデータベースに保存できます。auth
モデル—User
、Group
、Permission
—は互いにリンクされ、ContentType
にリンクされているため、同じ場所に保管する必要がありますContentType
としてデータベース。admin
はauth
に依存しているため、そのモデルはauth
と同じデータベースに存在する必要があります。flatpages
とredirects
はsites
に依存しているため、それらのモデルはsites
と同じデータベースに存在する必要があります。
さらに、一部のオブジェクトは、:djadmin: `migrate` がデータベースに保持するためのテーブルを作成した直後に、自動的に作成されます。
- デフォルトの
Site
、 - 各モデル(そのデータベースに保存されていないモデルを含む)の
ContentType
、 - 各モデルの
Permission
(そのデータベースに保存されていないモデルを含む)。
複数のデータベースを使用する一般的なセットアップでは、これらのオブジェクトを複数のデータベースに含めることは役に立ちません。 一般的なセットアップには、プライマリ/レプリカと外部データベースへの接続が含まれます。 したがって、これら3つのモデルを1つのデータベースにのみ同期できるデータベースルーターを作成することをお勧めします。 複数のデータベースにテーブルを配置する必要のないcontribアプリとサードパーティアプリにも同じアプローチを使用します。
警告
コンテンツタイプを複数のデータベースに同期する場合は、それらの主キーがデータベース間で一致しない可能性があることに注意してください。 これにより、データが破損したり、データが失われたりする可能性があります。