有没有办法指定模型(或应用程序,甚至)应该只使用一个特定的数据库?
我正在使用我不想更改的旧数据库。我有两个数据库 - 'default'是可以用于管理员等的sqlite数据库,也可以是旧数据库。我使用了inspectdb为遗留数据库(的一部分)创建了一个模型,它有managed = False
。但有没有办法在模型中指定它只适用于特定的数据库?
我看到你可以specify using=databasename
在某些查询集等中,但这对于Databrowse(以及可能还有通用视图?)这样的东西都没有用。可能是Databrowse的一个短缺,你不能指定一个数据库,但它似乎是指定它的正确位置是模型......
然后我想也许答案是编写一个只引用我遗留数据库的自定义model manager - 但是文档没有提到这样的内容。
对于Django世界,我是否只有一个不同的心理模型可以使用多个数据库?
答案 0 :(得分:12)
据我所知,您无法直接使用模型指定数据库,因为它会阻止应用程序重复使用,但是我可以在文档中看到:
答案 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)
它有一些次要的好处:
params
数据库中的模型,我们不需要default
嵌套类。Meta
嵌套类,它已经是Django的一部分。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
。