【问题标题】:Foreign key fields different databases. Django外键字段不同的数据库。姜戈
【发布时间】:2012-05-29 09:52:18
【问题描述】:

是否可以在不同的数据库中使用具有外键字段的模型? 示例:

class MultiBDModel(models.Model):
    db1_user = models.ForeignKey(User) # here suppose to be foreign key on `db1`
    db2_user = models.ForeignKey(User) # and here on `db2`

也许以某种方式复制用户。申请它的自定义经理。它返回使用 using='db1' 的查询集

settings.py:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'db1',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    },

    'website': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'db2',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.              # Set to empty string for default. Not used with sqlite3.
    }
}

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    没有。 ORM 不能做任何数据库引擎不能做的事情。

    【讨论】:

    • 好的,我明白了。我忘记了 django 是在数据库中而不是在 python 中进行连接。
    【解决方案2】:

    没有。正如@ignacio-vazquez-abrams 所写,一个模型必须在同一个数据库中包含所有字段。


    但是

    作为替代方案,您可以使用代理模型来链接来自两个不同数据库的模型。

    目标

    一个模型应该同时提供来自 db1 和 db2 的字段

    一般技巧

    • 您有来自 db1 的模型 ContactMessage,我们将其命名为 legacy_db。我们假设您不想接触此模型,因为它来自另一个项目。
    • 创建一个proxy modelProxyContactMessage,它与ContactMessage具有相同的属性。
    • 使用database router 告诉Django 在legacy_db 中查找ProxyContactMessage 对象的位置。
    • 添加一个新模型ExtendedContactMessage,其中包含您要添加的字段。用ProxyContactMessage 声明OneToOneField。此数据将保存到您的 db2 django_db
    • 您的代理模型不能保存新字段,因为它是抽象的,但它可以具有询问相关ExtendedContactMessage 对象(如果有)的方法。添加所需的可调用对象。

    示例

    在您的legacy_app/models.py 中,db1 legacy_db 上的模型为:

    class ContactMessage(models.Model):
        subject = models.CharField(max_length=255)
        message = models.TextField()
        created_at = models.DateTimeField()
        created_by = models.CharField(max_length=255)
    
        class Meta:
            managed = False
            db_table = 'contact_message'
    
        def __unicode__(self):
            return self.subject
    

    因此你在myapp/models.py中创建:

    class ProxyContactMessage(ContactMessage):
        class Meta:
            proxy = True
            verbose_name = 'Contact message'
            verbose_name_plural = 'Contact messages'
    
        def add_extension(self):
            e = ExtendedContactMessage(contact_message=self)
            e.save()
            return e
    
        def mark_as_processed(self):
            try:
                e = self.extendedcontactmessage
            except ExtendedContactMessage.DoesNotExist:
                e = self.add_extension()
            e.mark_as_processed()
    
        def processed(self):
            return self.extendedcontactmessage.processed
    
        def processed_at(self):
            return self.extendedcontactmessage.processed_at
    
    class ExtendedContactMessage(models.Model):
        contact_message = models.OneToOneField(ProxyContactMessage)
        processed = models.BooleanField(default=False, editable=False)
        processed_at = models.DateTimeField(null=True, default=None, editable=False)
    
        def mark_as_processed(self):
            self.processed = True
            self.processed_at = timezone.now()
            self.save()
    

    请注意,只有非抽象模型 ExtendedContactMessage 会保存在 db2 中,因为 ProxyContactMessage 是抽象的。

    settings.py中,用类设置DATABASE_ROUTERS

    class LegacyRouter(object):
        """
        A router to control all database operations on models in the
        legacy database.
        """
        def db_for_read(self, model, **hints):
            if model.__name__ == 'ProxyContactMessage':
                return 'legacy_db'
            return None
        def db_for_write(self, model, **hints):
            """
            Attempts to write in legacy DB for ContactMessage.
            """
            if model.__name__ == 'ProxyContactMessage':
                return 'legacy_db'
            return None
    

    您的默认路由器将所有内容发送到 db2。

    最后你可能有一个管理类:

    def mark_as_processed(modeladmin, request, queryset):
        for obj in queryset:
            obj.mark_as_processed()
    mark_as_processed.short_description = "Mark as processed"
    
    class ProxyContactMessageAdmin(admin.ModelAdmin):
        list_display = (
            'subject',
            'message',
            'created_at',
            'created_by',
            'processed',
            'processed_at',
        )
        actions = (mark_as_processed,)
    admin.site.register(ProxyContactMessage, ProxyContactMessageAdmin)
    

    相关:

    Use a router for the proxy class

    "Hack" the app_name in Meta

    Catch the queryset

    【讨论】:

      猜你喜欢
      • 2011-12-24
      • 2016-04-25
      • 1970-01-01
      • 1970-01-01
      • 2019-01-04
      • 2020-08-05
      • 2017-02-08
      • 2014-07-23
      • 2010-12-21
      相关资源
      最近更新 更多