47

モデル(またはアプリ)が特定のデータベースを1つだけ使用するように指定する方法はありますか?

変更したくないレガシーデータベースを使用しています。私は2つのデータベースを持っています-「デフォルト」はadminなどに使用できるsqliteデータベースとレガシーデータベースです。inspectdbを使用して、レガシーデータベース(の一部)のモデルを作成しました managed = False。しかし、モデル自体で、特定のデータベースにのみ適用されることを指定する方法はありますか?

一部のクエリセットなどで指定using=databasenameできるようですが、これはDatabrowse(および場合によっては汎用ビュー)などには適していません。データベースを指定できないのはDatabrowseの欠点かもしれませんが、それを指定するのに適切な場所はモデルのようです...

それから、おそらく答えは、私のレガシーデータベースのみを参照するカスタムモデルマネージャーを作成することだと思いましたが、ドキュメントにはそのようなことは何も記載されていません。

Djangoの世界とは異なる、複数のデータベースの使用方法に関するメンタルモデルがありますか?

4

6 に答える 6

63

モデルのデータベースを指定することはできませんが、カスタムDBルータークラスでデータベースを定義することはできます。

# app/models.py
class SomeModel(models.Model):
    ...

# app/dbrouters.py
from app.models import SomeModel
...
class MyDBRouter(object):

    def db_for_read(self, model, **hints):
        """ reading SomeModel from otherdb """
        if model == SomeModel:
            return 'otherdb'
        return None

    def db_for_write(self, model, **hints):
        """ writing SomeModel to otherdb """
        if model == SomeModel:
            return 'otherdb'
        return None


# app/settings.py
DATABASE_ROUTERS = ('app.dbrouters.MyDBRouter',)
...
DATABASES = {
    ...
    'otherdb': {
        ....
    }
}
于 2017-12-19T22:20:46.100 に答える
16

私の知る限り、モデルでデータベースを直接指定することはできません。これは、アプリが再利用できないようにするためですが、ドキュメントで確認できることからです。

https://docs.djangoproject.com/en/1.8/topics/db/multi-db/

于 2010-08-19T07:56:35.460 に答える
10

このマネージャーを使用すると、モデルを非常に簡単にルーティングできることがわかりました。

class SecondDbManager(models.Manager):
    def get_queryset(self):
        qs = super().get_queryset()

        # if `use_db` is set on model use that for choosing the DB
        if hasattr(self.model, 'use_db'):
            qs = qs.using(self.model.use_db)

        return qs

use_db='databasename'このマネージャーをモデルに追加するだけで機能します。

または、さらに単純化するために、そのベースモデルを作成しました。

class SecondDbBase(models.Model):
    use_db = 'my_second_db'
    objects = SecondDbManager()

    class Meta:
        abstract = True

そしてこれであなたがする必要があるのはそのようにそれを拡張することです。それ以外の:

class Customer(models.Model):

これを行うだけで機能します:

class Customer(SecondDbBase):

PS。それが良い習慣なのか最良の解決策なのかはわかりませんが、それは機能し、他のデータベースへのルーティングは簡単です:)

PPS。私はこれまで、Django(managed = False)によって管理されていないテーブルの読み取りと書き込みにのみこれらを使用したことがあるので、それらの移行を作成する必要がある場合、それが機能するかどうかはわかりません。それでも使用する必要があるかもしれませんDATABASE_ROUTERS

于 2019-04-18T22:40:02.390 に答える
10

簡単な解決策は、モデルに常に特定のデータベースを使用するようにマネージャーを設定することです。Djangoのを調べてくださいusing

例:

class User(models.Model):
    birth_date = models.DateField()

    class Meta:
        managed = False
        db_table = 'myotherapp_user'

User.objects = User.objects.using('myotherdb')

その後、を使用できUser.objects、常にの'myotherdb'代わりにデータベースを使用します'default'

異なるデータベースのモデル間の関係は機能しないことに注意してください。ただし、これはそのままではサポートされていないため、Djangoの問題です。

于 2020-01-13T15:20:04.333 に答える
4

Markの優れた回答に基づいて構築-モデルの属性に基づいて、モデルの読み取り/書き込み操作と移行の両方を異なるデータベースにルーティングする方法は次のとおりです

# app/models.py
class SomeModel(models.Model):
    class params:
        db = 'default'

class SomeOtherDbModel(models.Model):
    class params:
        db = 'otherdb'
    ...


# app/dbrouters.py
import app.models
allmodels = dict([(name.lower(), cls) for name, cls in app.models.__dict__.items() if isinstance(cls, type)])
...
class MyDBRouter(object):

    def db_for_read(self, model, **hints):
        """ reading model based on params """
        return getattr(model.params, 'db')

    def db_for_write(self, model, **hints):
        """ writing model based on params """
        return getattr(model.params, 'db')

    def allow_migrate(self, db, app_label, model_name = None, **hints):
        """ migrate to appropriate database per model """
        model = allmodels.get(model_name)
        return(model.params.db == db)


# app/settings.py
DATABASE_ROUTERS = ('app.dbrouters.MyDBRouter',)
...
DATABASES = {
    ...
    'otherdb': {
        ....
    }
}

こちらのドキュメントを参照してください:https ://docs.djangoproject.com/en/3.0/topics/db/multi-db/#database-routers

model_name引数は、実行時に小文字として渡されるmodel.__name__ため、この属性を小文字にキャストしてルックアップディクショナリを構築したのはなぜですか。

その後、移行は次のように実行する必要があります

python3 manage.py migrate app --database default
python3 manage.py migrate app --database otherdb
于 2020-02-28T14:02:30.540 に答える
2

Django2.2とpytestでテストされています。
ちょっとだけ合理化するために、優れた@ chris-schonの答え

class LegacyDbModel(models.Model):

    class Meta:
        abstract = True
        _db = 'legacy_db_alias'


class LegacyDbRouter(object):

    def db_for_read(self, model, **hints):
        """ reading model based on params """
        if not hasattr(model, 'Meta'):
            return None
        return getattr(model.Meta, '_db', None)

    def db_for_write(self, model, **hints):
        """ writing model based on params """
        if not hasattr(model, 'Meta'):
            return None
        return getattr(model.Meta, '_db', None)

これにはいくつかの小さな利点があります。

  1. DBparamsのモデルにネストされたクラスは必要ありません。default
  2. MetaすでにDjangoの一部であるネストされたクラスを活用します。
  3. ルーターallow_migrateはテストなしでも動作します。

唯一の欠点は、Meta明示的に継承する必要があることです。

class LegacyModel(LegacyDbModel):
    # ... docs, fields, etc.

    class Meta(LegacyDbModel.Meta):
        managed = False
        db_table = 'legacy_table'

しかし、それでもDjangoの動作とより一致しています
覚えておいてください

明示的は暗黙的よりも優れています。

于 2020-10-02T11:11:38.987 に答える