I know Django does not support foreign keys across multiple databases (originally Django 1.3 docs)
But I\'m looking for a workaround.
A foreign key field implies that you can - query on the relationship by joining ie fruit__name - check referential integrity - ensure referential integrity upon deletes - admin raw id lookup functionality - (some more...)
The first use case would always be problematic. Probably there are some other foreign key special cases in the codebase which also wouldn't work.
I run a rather large django site and we are currently using a plain integerfield. For now i would think subclassing the integerfield and adding the id to object conversion would be easiest (in 1.2 that required patching some bits of django, hope that improved by now) Will let you know what solution we find.
This solution is originally written for one managed database with migrations and one or more legacy databases with models Meta managed=False
connected at database level to the same database. If a db_table
option contains a database name plus table name quoted correctly by ' ` ' (MySQL) or by ' " ' (other db), e.g. db_table = '"DB2"."table_b"'
, then it is not quoted any more by Django. Queries are compiled by Django ORM correctly, even with JOINs:
class TableB(models.Model):
....
class Meta:
db_table = '`DB2`.`table_b`' # for MySQL
# db_table = '"DB2"."table_b"' # for all other backends
managed = False
Query set:
>>> qs = TableB.objects.all()
>>> str(qs.query)
'SELECT "DB2"."table_b"."id" FROM DB2"."table_b"'
That is supported by all db backends in Django.
(It seems that I started a bounty on a duplicate new question where my answer continues.)
I know that Djano-nosql has support for keys and such though some magic from http://www.allbuttonspressed.com/projects/django-dbindexer. Maybe some of that could help.
From the description:
"you can just tell the dbindexer which models and fields should support these queries and it'll take care of maintaining the required indexes for you."
-Kerry
I have a new solution for django v1.10. There are two parts. It works with django.admin and django.rest-framework.
ForeignKey
class and create ForeignKeyAcrossDb
, and override the validate()
function, based on this ticket and this post.class ForeignKeyAcrossDb(models.ForeignKey):
def validate(self, value, model_instance):
if self.remote_field.parent_link:
return
super(models.ForeignKey, self).validate(value, model_instance)
if value is None:
return
using = router.db_for_read(self.remote_field.model, instance=model_instance)
qs = self.remote_field.model._default_manager.using(using).filter(
**{self.remote_field.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.remote_field.model._meta.verbose_name, 'pk': value,
'field': self.remote_field.field_name, 'value': value,
}, # 'pk' is included for backwards compatibility
)
db_constraint=False
, for example, album=ForeignKeyAcrossDb(Singer, db_constraint=False, on_delete=models.DO_NOTHING)
You could make a view in the database that has the cross database query in it, then define the model for the view in a separate file to keep syncdb working.
Happy programming. :)
After breaking my head some days, I managed to get my Foreign Key ON THE SAME BANK!
Can be made a change over the FORM to seek a FOREIGN KEY in a different bank!
First, add a RECHARGE of FIELDS, both directly (crack) my form, in function ____init____
app.form.py
# -*- coding: utf-8 -*-
from django import forms
import datetime
from app_ti_helpdesk import models as mdp
#classe para formulario de Novo HelpDesk
class FormNewHelpDesk(forms.ModelForm):
class Meta:
model = mdp.TblHelpDesk
fields = (
"problema_alegado",
"cod_direcionacao",
"data_prevista",
"hora_prevista",
"atendimento_relacionado_a",
"status",
"cod_usuario",
)
def __init__(self, *args, **kwargs):
#-------------------------------------
# using remove of kwargs
#-------------------------------------
db = kwargs.pop("using", None)
# CASE use Unique Keys
self.Meta.model.db = db
super(FormNewHelpDesk, self).__init__(*args,**kwargs)
#-------------------------------------
# recreates the fields manually
from copy import deepcopy
self.fields.update(deepcopy( forms.fields_for_model( self.Meta.model, self.Meta.fields, using=db ) ))
#
#-------------------------------------
#### follows the standard template customization, if necessary
self.fields['problema_alegado'].widget.attrs['rows'] = 3
self.fields['problema_alegado'].widget.attrs['cols'] = 22
self.fields['problema_alegado'].required = True
self.fields['problema_alegado'].error_messages={'required': 'Necessário informar o motivo da solicitação de ajuda!'}
self.fields['data_prevista'].widget.attrs['class'] = 'calendario'
self.fields['data_prevista'].initial = (datetime.timedelta(4)+datetime.datetime.now().date()).strftime("%Y-%m-%d")
self.fields['hora_prevista'].widget.attrs['class'] = 'hora'
self.fields['hora_prevista'].initial =datetime.datetime.now().time().strftime("%H:%M")
self.fields['status'].initial = '0' #aberto
self.fields['status'].widget.attrs['disabled'] = True
self.fields['atendimento_relacionado_a'].initial = '07'
self.fields['cod_direcionacao'].required = True
self.fields['cod_direcionacao'].label = "Direcionado a"
self.fields['cod_direcionacao'].initial = '2'
self.fields['cod_direcionacao'].error_messages={'required': 'Necessário informar para quem é direcionado a ajuda!'}
self.fields['cod_usuario'].widget = forms.HiddenInput()
calling the Form from the View
app.view.py
form = forms.FormNewHelpDesk(request.POST or None, using=banco)
Now, the change in the source Code DJANGO
Only fields of type ForeignKey, ManyToManyField and OneToOneField can use the 'using', so added an IF ...
django.forms.models.py
# line - 133: add using=None
def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, using=None):
# line - 159
if formfield_callback is None:
#----------------------------------------------------
from django.db.models.fields.related import (ForeignKey, ManyToManyField, OneToOneField)
if type(f) in (ForeignKey, ManyToManyField, OneToOneField):
kwargs['using'] = using
formfield = f.formfield(**kwargs)
#----------------------------------------------------
elif not callable(formfield_callback):
raise TypeError('formfield_callback must be a function or callable')
else:
formfield = formfield_callback(f, **kwargs)
ALTER FOLLOW FILE
django.db.models.base.py
alter
# line 717
qs = model_class._default_manager.filter(**lookup_kwargs)
for
# line 717
qs = model_class._default_manager.using(getattr(self, 'db', None)).filter(**lookup_kwargs)
Ready :D