【问题标题】:"Matching"/relations data across databases in DjangoDjango中跨数据库的“匹配”/关系数据
【发布时间】:2014-08-29 00:12:51
【问题描述】:

在开发索引系统文档的网站时,我遇到了一个棘手的问题,即要破解 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,但是我的印象是,这与使用哪个数据库的场景无关。

【问题讨论】:

    标签: python django postgresql django-models


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      你说得对,跨数据库的外键是 Django ORM 中的一个问题,在某种程度上也是 db 级别的问题。

      您基本上已经有了答案:“我在 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 模型的所有读取路由到适当的数据库。

      【讨论】:

        【解决方案3】:

        我会选择方法 get_system()。所以:

        class Document:
            def get_system(self):
               return System.objects.using('remote').get(system_id=self.system_id)
        

        这是最简单的解决方案。一个可能的解决方案是使用 PostgreSQL 的外部数据包装器功能。通过使用 FDW,您可以从 django 中抽象出 multidb 处理并在数据库中进行 - 现在您可以使用需要使用文档的查询 -> 系统关系。

        最后,如果您的用例允许,只需定期将系统数据复制到本地数据库即可。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-25
          • 1970-01-01
          • 1970-01-01
          • 2021-03-01
          • 1970-01-01
          相关资源
          最近更新 更多