如何在不同的DB中使用带有外键的django模型?

Ser*_*gey 18 mysql django foreign-keys django-models

我有两个不同数据库的2个模型:
数据库是手动创建的,但它不会改变任何东西.

class LinkModel(models.Model): # in 'urls' database
    id = models.AutoField(primary_key=True)
    host_id = models.IntegerField()
    path = models.CharField(max_length=255)

    class Meta:
        db_table = 'links'
        app_label = 'testapp'

    def __unicode__(self):
        return self.path

class NewsModel(models.Model):  # in default database
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50)
    link = models.ForeignKey(LinkModel)

    class Meta:
        db_table = 'news'
        app_label = 'test'

    def __unicode__(self):
        return self.title
Run Code Online (Sandbox Code Playgroud)

在以下代码之后出现错误

newsItem, created = NewsModel.objects.get_or_create( title="test" )
link = LinkModel.objects.using('urls').get( id=1 )
newsItem.link = link  # error!

 Cannot assign "<LinkModel: />": instance is on database "default", value is on database "urls"
Run Code Online (Sandbox Code Playgroud)

为什么我不能使用外键和不同数据库的模型?

Vit*_*eev 13

跨数据库限制

Django目前不提供跨越多个数据库的外键或多对多关系的任何支持.如果已使用路由器将模型分区到不同的数据库,则由这些模型定义的任何外键和多对多关系必须位于单个数据库的内部.

Django - 多个数据库的限制

麻烦

同样的麻烦.ForeignKey()类中的错误.

在validate()方法中.

看票

v1.2,v1.3,v1.4rc1中存在错误

试试这个补丁来解决它.


Jar*_*ott 9

The Problem

*Note: this is an extension of Vitaly Fadeev's answer

Due to a desire to keep referential integrity, Django does not allow for foreign keys which span multiple databases: https://docs.djangoproject.com/en/dev//topics/db/multi-db/#limitations-of-multiple-databases. Although this is desired in 99% of all applications, in some cases it is helpful to be able to create such an association in the ORM even if you cannot ensure referential integrity.

A Solution

我已经创建了一个使用提出的解决方案梗概这里通过维塔利·法捷耶夫包装成Django的ForeignKey的领域的一个子类.此解决方案不需要您修改Django Core文件,而是在您需要的情况下使用此字段类型.

示例用法

# app1/models.py
from django.db import models

class ClientModel(models.Model)
    name = models.CharField()
    class Meta:
        app_label = 'app1'

# app2/models.py
from django.db import models
from some_location.related import SpanningForeignKey

class WidgetModel(models.Model):
    client = SpanningForeignKey('app1.ClientModel', default=None, null=True,
                                blank=True, verbose_name='Client')
Run Code Online (Sandbox Code Playgroud)

要旨

要点可以在这里找到:https://gist.github.com/gcko/de1383080e9f8fb7d208

复制到这里是为了方便访问:

from django.core import exceptions
from django.db.models.fields.related import ForeignKey
from django.db.utils import ConnectionHandler, ConnectionRouter

connections = ConnectionHandler()
router = ConnectionRouter()


class SpanningForeignKey(ForeignKey):

    def validate(self, value, model_instance):
        if self.rel.parent_link:
            return
        # Call the grandparent rather than the parent to skip validation
        super(ForeignKey, self).validate(value, model_instance)
        if value is None:
            return

        using = router.db_for_read(self.rel.to, instance=model_instance)
        qs = self.rel.to._default_manager.using(using).filter(
            **{self.rel.field_name: value}
        )
        qs = qs.complex_filter(self.get_limit_choices_to())
        if not qs.exists():
            raise exceptions.ValidationError(
                self.error_messages['invalid'],
                code='invalid',
                params={
                    'model': self.rel.to._meta.verbose_name, 'pk': value,
                    'field': self.rel.field_name, 'value': value,
                },  # 'pk' is included for backwards compatibility
            )
Run Code Online (Sandbox Code Playgroud)