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

时间:2010-08-19 06:06:51

标签: django django-models multiple-databases django-database

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

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

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

然后我想也许答案是编写一个只引用我遗留数据库的自定义model manager - 但是文档没有提到这样的内容。

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

6 个答案:

答案 0 :(得分:12)

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

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

答案 1 :(得分:12)

您无法为模型指定数据库,但您可以在自定义数据库路由器类中定义它。

# 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': {
        ....
    }
}

答案 2 :(得分:5)

简单的解决方案是将管理器设置为始终对模型使用特定的数据库。查看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的问题,因为它不支持现成的。

答案 3 :(得分:3)

基于Mark的出色答案-这是基于模型的属性将模型的读/写操作和迁移路由到不同数据库的方法

%LocalAppData%\Microsoft\dotnet

在此处查看文档:{​​{3}}

model_name参数在运行时作为# 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': { .... } } 的小写字母传递,因此为什么我们通过将此属性强制转换为lower来构建查找字典。

然后应以以下方式运行迁移

model.__name__

答案 4 :(得分:1)

经过Django 2.2和pytest的测试。
只是为了简化一些出色的@ chris-schon answer

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. 对于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'

但是甚至更多aligned with how Django works
Remember

显式优于隐式。

答案 5 :(得分:0)

我发现您可以使用此管理器轻松路由模型:

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 = SecureDbManager()

    class Meta:
        abstract = True

您需要做的就是像这样扩展它。代替:

class Customer(models.Model):

只要这样做,它就会起作用:

class Customer(SecondDbBase):

PS。我不确定这是一个好的做法还是最好的解决方案,但是它是否有效,并且路由到其他数据库很容易:)

PSS。我仅将它们用于仅读写不受Django(managed = False)管理的表,因此,如果您需要为它们创建迁移,则不知道它是否有效。可能仍需要使用DATABASE_ROUTERS