"在Django中跨数据库匹配" /关系数据

时间:2014-07-08 09:51:45

标签: python django postgresql django-models

在开发一个索引系统文档的网站时,我遇到了一个难以解决的问题,即关于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,但我的印象是它与这个使用数据库的场景并不相关。

3 个答案:

答案 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调用。