在开发一个索引系统文档的网站时,我遇到了一个难以解决的问题,即关于Django中数据和数据库之间的匹配关系。
我本地数据库的简化模型:
from django.db import models
class Document(models.Model):
name = models.CharField(max_length=200)
system_id = models.IntegerField()
...
想象的模型,系统细节存储在远程数据库中。
from django.db import models
class System(models.Model):
name = models.CharField(max_length=200)
system_id = models.IntegerField()
...
我的想法是,在我的网站上创建一个新的文档条目时,相关系统的ID将存储在本地数据库中。在呈现数据时,我必须使用存储的ID从远程数据库中检索系统名称和其他详细信息。
我已经调查了foreign keys across databases,但这似乎非常广泛,我不确定我是否想要关系。相反,我可视化Document模型/类中的一个函数,该函数能够检索匹配的数据,例如通过导入自定义路由器/函数。
我将如何解决这个问题?
请注意,我无法更改远程数据库上的任何内容,并且它是只读的。不确定我是否应该为System创建一个模型。这两个数据库都使用PostgreSQL,但我的印象是它与这个使用数据库的场景并不相关。
答案 0 :(得分:4)
在django文档中multi-db (manually-selecting-a-database)
# This will run on the 'default' database.
Author.objects.all()
# So will this.
Author.objects.using('default').all()
# This will run on the 'other' database.
Author.objects.using('other').all()
The 'default' and 'other' are aliases for you databases.
In your case it would could be 'default' and 'remote'.
当然你可以用你想要的任何东西替换.all()。
Example: System.objects.using('remote').get(id=123456)
答案 1 :(得分:1)
我会选择方法get_system()。所以:
class Document:
def get_system(self):
return System.objects.using('remote').get(system_id=self.system_id)
这是最简单的解决方案。一种可能的解决方案也是使用PostgreSQL的外部数据包装器功能。通过使用FDW,您可以从django中抽象出multidb处理并在数据库中执行 - 现在您可以使用需要使用该文档的查询 - >系统关系。
最后,如果您的用例允许,只需定期将系统数据复制到本地数据库就可以了。
答案 2 :(得分:1)
你是正确的,跨数据库的外键是Django ORM中的一个问题,在某种程度上也是数据库级别的问题。
你已经基本上得到了答案:"我在Document模型/类中可视化一个能够检索匹配数据的函数"
我这样做:
class RemoteObject(object):
def __init__(self, remote_model, remote_db, field_name):
# assumes remote db is defined in Django settings and has an
# associated Django model definition:
self.remote_model = remote_model
self.remote_db = remote_db
# name of id field on model (real db field):
self.field_name = field_name
# we will cache the retrieved remote model on the instance
# the same way that Django does with foreign key fields:
self.cache_name = '_{}_cache'.format(field_name)
def __get__(self, instance, cls):
try:
rel_obj = getattr(instance, self.cache_name)
except AttributeError:
system_id = getattr(instance, self.field_name)
remote_qs = self.remote_model.objects.using(self.remote_db)
try:
rel_obj = remote_qs.get(id=system_id)
except self.remote_model.DoesNotExist:
rel_obj = None
setattr(instance, self.cache_name, rel_obj)
if rel_obj is None:
raise self.related.model.DoesNotExist
else:
return rel_obj
def __set__(self, instance, value):
setattr(instance, self.field_name, value.id)
setattr(instance, self.cache_name, value)
class Document(models.Model:
name = models.CharField(max_length=200)
system_id = models.IntegerField()
system = RemoteObject(System, 'system_db_name', 'system_id')
您可能会发现上面的RemoteObject
类实现了Python的描述符协议,有关详细信息,请参阅此处:
https://docs.python.org/2/howto/descriptor.html
使用示例:
>>> doc = Document.objects.get(pk=1)
>>> print doc.system_id
3
>>> print doc.system.id
3
>>> print doc.system.name
'my system'
>>> other_system = System.objects.using('system_db_name').get(pk=5)
>>> doc.system = other_system
>>> print doc.system_id
5
更进一步,您可以编写自定义数据库路由器:
https://docs.djangoproject.com/en/dev/topics/db/multi-db/#using-routers
这可以让您通过将using('system_db_name')
模型的所有读取路由到相应的数据库来消除代码中的System
调用。