1

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

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.
    }
}
4

2 回答 2

2

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


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

目标

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

一般技巧

  • 您有ContactMessage来自 db1 的模型,我们将命名为legacy_db. 我们假设您不想接触此模型,因为它来自另一个项目。
  • 创建一个代理模型 ProxyContactMessage,它具有相同的属性ContactMessage
  • 使用数据库路由器告诉 Django 在哪里查找legacy_db对象ProxyContactMessage
  • ExtendedContactMessage使用您要添加的字段添加新模型。OneToOneField用声明一个ProxyContactMessage。此数据将保存到您的 db2django_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)

有关的:

为代理类使用路由器

“破解” Meta 中的 app_name

捕获查询集

于 2017-06-20T16:02:06.883 回答
1

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

于 2012-05-22T20:51:31.167 回答