外键字段不同的数据库。 Django的

时间:2012-05-22 20:48:40

标签: django django-models

是否可以在不同的数据库上使用外键字段的模型? 例如:

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`

也许以某种方式复制用户。申请自定义经理。使用='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.
    }
}

2 个答案:

答案 0 :(得分:1)

没有。 ORM无法执行数据库引擎无法执行的任何操作。

答案 1 :(得分:1)

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

BUT

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

目标

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

一般技巧

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

实施例

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