Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use django models with foreign keys in different DBs?

Tags:

I have 2 models for 2 different databases:
Databases were created manually but it should change nothing.

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 

After the following code an error raises

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" 

Why can't I use foreign key and a model for different database?

like image 800
Sergey Avatar asked Jul 26 '11 13:07

Sergey


People also ask

Can you use multiple databases within the same Django project?

Django's admin doesn't have any explicit support for multiple databases. If you want to provide an admin interface for a model on a database other than that specified by your router chain, you'll need to write custom ModelAdmin classes that will direct the admin to use a specific database for content.

Can a model have two foreign keys in Django?

Your intermediate model must contain one - and only one - foreign key to the source model (this would be Group in our example), or you must explicitly specify the foreign keys Django should use for the relationship using ManyToManyField.

Does Django automatically index foreign keys?

Django automatically creates an index for all models. ForeignKey columns. From Django documentation: A database index is automatically created on the ForeignKey .


2 Answers

Cross-database limitations

Django doesn't currently provide any support for foreign key or many-to-many relationships spanning multiple databases. If you have used a router to partition models to different databases, any foreign key and many-to-many relationships defined by those models must be internal to a single database.

Django - limitations-of-multiple-databases

Trouble

Same trouble. Bug in ForeignKey() class.

In validate() method.

See ticket

Bug exists in v1.2, v1.3, v1.4rc1

Solution

Try this patch to solve it.

like image 86
Vitaly Fadeev Avatar answered Oct 06 '22 00:10

Vitaly Fadeev


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

I have created a Gist that uses the solution proposed here by Vitaly Fadeev wrapped as a subclass of the Django ForeignKey field. This solution does not require you to modify Django Core files but instead use this field type instead in the cases you need it.

Example Usage

# 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') 

The Gist

The gist is available here: https://gist.github.com/gcko/de1383080e9f8fb7d208

Copied here for ease of access:

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             ) 
like image 29
Jared Scott Avatar answered Oct 05 '22 23:10

Jared Scott