47

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

我正在使用不想更改的旧数据库。我有两个数据库 - “默认”是一个可用于管理员等的 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

简单的解决方案是将管理器设置为始终为模型使用特定的数据库。查看 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 回答
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。我不确定这是一个好的做法还是最好的解决方案,但它可以工作并且路由到其他数据库是轻而易举的 :)

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

于 2019-04-18T22:40:02.390 回答
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

用 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)

它有几个小好处:

  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 回答