Django - 如何为模型指定数据库?

pfc*_*ise 25 django django-models multiple-databases django-database

有没有办法指定模型(或应用程序,甚至)应该只使用一个特定的数据库?

我正在使用一个我不想改变的遗留数据库.我有两个数据库 - 'default'是可以用于管理员等的sqlite数据库,也可以是旧数据库.我使用了inspectdb为遗留数据库(的一部分)创建了一个模型,它有 managed = False.但有没有办法在模型中指定它只适用于特定的数据库?

我看到你可以在一些查询集等中指定using=databasename,但这对像Databrowse这样的东西是不利的(也可能是通用视图?).可能是Databrowse的一个短缺,你不能指定一个数据库,但它似乎是指定它的正确位置是模型...

然后我想也许答案是编写一个只引用我的遗留数据库的自定义模型管理器 - 但是文档没有提到类似的东西.

对于Django世界,我是否只有一个不同的心理模型可以使用多个数据库?

Jua*_*own 27

您不能为模型指定数据库,但可以在自定义数据库路由器类中定义它.

# 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': {
        ....
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案 (2认同)

Hor*_*ann 12

据我所知,您无法直接使用模型指定数据库,因为它会阻止应用程序重复使用,但是我可以在文档中看到:

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


rai*_*n01 9

我发现您可以使用此管理器非常简单地路由模型:

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
Run Code Online (Sandbox Code Playgroud)

只需将use_db='databasename'这个管理器添加到您的模型中即可。

或者为了进一步简化它,我为它创建了一个基本模型:

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

    class Meta:
        abstract = True
Run Code Online (Sandbox Code Playgroud)

有了这个,你需要做的就是像这样扩展它。代替:

class Customer(models.Model):
Run Code Online (Sandbox Code Playgroud)

只需这样做,它就可以工作:

class Customer(SecondDbBase):
Run Code Online (Sandbox Code Playgroud)

附注。我不确定这是一个好的做法还是最好的解决方案,但它可以工作并且路由到其他数据库是轻而易举的 :)

聚苯乙烯。我只将这些用于读取和写入不由 Django( managed = False)管理的表,因此如果您需要为它们创建迁移,我不确定它是否有效。可能仍然需要使用DATABASE_ROUTERS它。


Pit*_*kos 9

简单的解决方案是将管理器设置为始终为模型使用特定的数据库。查看 Django 的using.

例子:

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

    class Meta:
        managed = False
        db_table = 'myotherapp_user'

User.objects = User.objects.using('myotherdb')
Run Code Online (Sandbox Code Playgroud)

然后你可以使用User.objects它,它会一直使用'myotherdb'数据库而不是'default'.

请注意,来自不同数据库的模型之间的关系将不起作用,但这是 Django 的一个问题,因为它不支持开箱即用。


Den*_*kiy 7

使用 Django 2.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)
Run Code Online (Sandbox Code Playgroud)

它有几个小好处:

  1. 我们不需要params来自数据库的模型的嵌套类default
  2. 我们利用Meta已经是 Django 一部分的嵌套类。
  3. allow_migrate路由器甚至无需测试即可工作。

唯一的缺点是我们需要显式继承Meta

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

    class Meta(LegacyDbModel.Meta):
        managed = False
        db_table = 'legacy_table'
Run Code Online (Sandbox Code Playgroud)

但即使这样也更符合 Django 的工作方式
记住

显式的比隐式的好。


小智 5

以 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': {
        ....
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅此处的文档:https : //docs.djangoproject.com/en/3.0/topics/db/multi-db/#database-routers

model_name 参数在运行时作为 的小写传递model.__name__,因此我们通过将此属性转换为lower来构建查找字典。

迁移应该作为

python3 manage.py migrate app --database default
python3 manage.py migrate app --database otherdb
Run Code Online (Sandbox Code Playgroud)