如何解决Django中缺少跨数据库外键支持的问题
我知道(最初是Django 1.3文档) 但我正在寻找解决办法 什么不起作用 我在一个单独的数据库中有两个模型 路由器.py:如何解决Django中缺少跨数据库外键支持的问题,django,django-models,django-orm,Django,Django Models,Django Orm,我知道(最初是Django 1.3文档) 但我正在寻找解决办法 什么不起作用 我在一个单独的数据库中有两个模型 路由器.py: class NewsRouter(object): def db_for_read(self, model, **hints): if model._meta.app_label == 'news_app': return 'news_db' return None def db_for_writ
class NewsRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'news_app' or obj2._meta.app_label == 'news_app':
return True
return None
def allow_syncdb(self, db, model):
if db == 'news_db':
return model._meta.app_label == 'news_app'
elif model._meta.app_label == 'news_app':
return False
return None
class NewsRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'news_app' or obj2._meta.app_label == 'news_app':
return True
return None
def allow_syncdb(self, db, model):
if db == 'news_db':
return model._meta.app_label == 'news_app'
elif model._meta.app_label == 'news_app':
return False
return None
class FruitRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'fruit_app':
return 'default'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'fruit_app':
return 'default'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'fruit_app' or obj2._meta.app_label == 'fruit_app':
return True
return None
def allow_syncdb(self, db, model):
if db == 'default':
return model._meta.app_label == 'fruit_app'
elif model._meta.app_label == 'fruit_app':
return False
return None
fruit_app/models.py中的型号1:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
class Article(models.Model):
fruit = models.ForeignKey('fruit_app.Fruit')
intro = models.TextField()
from django.db import models
class FruitManager(models.Manager):
forced_using = 'default'
class Fruit(models.Model):
name = models.CharField(max_length=20)
objects = FruitManager()
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
新闻应用程序/models.py中的模型2:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
class Article(models.Model):
fruit = models.ForeignKey('fruit_app.Fruit')
intro = models.TextField()
from django.db import models
class FruitManager(models.Manager):
forced_using = 'default'
class Fruit(models.Model):
name = models.CharField(max_length=20)
objects = FruitManager()
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
试图在管理员中添加“文章”会出现以下错误,因为它正在错误的数据库中查找水果模型('news\u db'
):
方法1:子类IntegerField
我创建了一个自定义字段ForeignKeyAcrossDb,它是IntegerField的一个子类。代码位于github上的以下位置:
fields.py:
from django.db import models
class ForeignKeyAcrossDb(models.IntegerField):
'''
Exists because foreign keys do not work across databases
'''
def __init__(self, model_on_other_db, **kwargs):
self.model_on_other_db = model_on_other_db
super(ForeignKeyAcrossDb, self).__init__(**kwargs)
def to_python(self, value):
# TODO: this db lookup is duplicated in get_prep_lookup()
if isinstance(value, self.model_on_other_db):
return value
else:
return self.model_on_other_db._default_manager.get(pk=value)
def get_prep_value(self, value):
if isinstance(value, self.model_on_other_db):
value = value.pk
return super(ForeignKeyAcrossDb, self).get_prep_value(value)
def get_prep_lookup(self, lookup_type, value):
# TODO: this db lookup is duplicated in to_python()
if not isinstance(value, self.model_on_other_db):
value = self.model_on_other_db._default_manager.get(pk=value)
return super(ForeignKeyAcrossDb, self).get_prep_lookup(lookup_type, value)
from django.db import models
from django.db import router
from django.db.models.query import QuerySet
class ReverseSingleRelatedObjectDescriptor(object):
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# a single "remote" value, on the class that defines the related field.
# In the example "choice.poll", the poll attribute is a
# ReverseSingleRelatedObjectDescriptor instance.
def __init__(self, field_with_rel):
self.field = field_with_rel
def __get__(self, instance, instance_type=None):
if instance is None:
return self
cache_name = self.field.get_cache_name()
try:
return getattr(instance, cache_name)
except AttributeError:
val = getattr(instance, self.field.attname)
if val is None:
# If NULL is an allowed value, return it.
if self.field.null:
return None
raise self.field.rel.to.DoesNotExist
other_field = self.field.rel.get_related_field()
if other_field.rel:
params = {'%s__pk' % self.field.rel.field_name: val}
else:
params = {'%s__exact' % self.field.rel.field_name: val}
# If the related manager indicates that it should be used for
# related fields, respect that.
rel_mgr = self.field.rel.to._default_manager
db = router.db_for_read(self.field.rel.to, instance=instance)
if getattr(rel_mgr, 'forced_using', False):
db = rel_mgr.forced_using
rel_obj = rel_mgr.using(db).get(**params)
elif getattr(rel_mgr, 'use_for_related_fields', False):
rel_obj = rel_mgr.using(db).get(**params)
else:
rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
setattr(instance, cache_name, rel_obj)
return rel_obj
def __set__(self, instance, value):
raise NotImplementedError()
class ForeignKeyAcrossDb(models.ForeignKey):
def contribute_to_class(self, cls, name):
models.ForeignKey.contribute_to_class(self, cls, name)
setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self))
if isinstance(self.rel.to, basestring):
target = self.rel.to
else:
target = self.rel.to._meta.db_table
cls._meta.duplicate_targets[self.column] = (target, "o2m")
def validate(self, value, model_instance):
pass
from django.core import exceptions
from django.db import models
from django.db import router
class ForeignKeyAcrossDb(models.ForeignKey):
def validate(self, value, model_instance):
if self.rel.parent_link:
return
models.Field.validate(self, value, model_instance)
if value is None:
return
using = router.db_for_read(self.rel.to, instance=model_instance) # is this more correct than Django's 1.2.5 version?
qs = self.rel.to._default_manager.using(using).filter(
**{self.rel.field_name: value}
)
qs = qs.complex_filter(self.rel.limit_choices_to)
if not qs.exists():
raise exceptions.ValidationError(self.error_messages['invalid'] % {
'model': self.rel.to._meta.verbose_name, 'pk': value})
我将我的文章模式改为:
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
问题是,有时当我访问Article.fruit时,它是一个整数,有时它是水果对象。我希望它始终是一个水果对象。我需要做什么才能使访问Article.fruit始终返回一个水果对象
作为解决方案的解决方案,我添加了一个fruit\u obj
属性,但如果可能的话,我想消除这个问题:
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
# TODO: shouldn't need fruit_obj if ForeignKeyAcrossDb field worked properly
@property
def fruit_obj(self):
if not hasattr(self, '_fruit_obj'):
# TODO: why is it sometimes an int and sometimes a Fruit object?
if isinstance(self.fruit, int) or isinstance(self.fruit, long):
print 'self.fruit IS a number'
self._fruit_obj = Fruit.objects.get(pk=self.fruit)
else:
print 'self.fruit IS NOT a number'
self._fruit_obj = self.fruit
return self._fruit_obj
def fruit_name(self):
return self.fruit_obj.name
方法2:子类ForeignKey字段
作为第二次尝试,我尝试将ForeignKey字段子类化。我修改了reversesingrelatedObjectDescriptor
以使用Fruit
的模型管理器上的forced\u指定的数据库。我还删除了ForeignKey
子类上的validate()
方法。此方法与方法1没有相同的问题。github上的代码位于:
fields.py:
from django.db import models
class ForeignKeyAcrossDb(models.IntegerField):
'''
Exists because foreign keys do not work across databases
'''
def __init__(self, model_on_other_db, **kwargs):
self.model_on_other_db = model_on_other_db
super(ForeignKeyAcrossDb, self).__init__(**kwargs)
def to_python(self, value):
# TODO: this db lookup is duplicated in get_prep_lookup()
if isinstance(value, self.model_on_other_db):
return value
else:
return self.model_on_other_db._default_manager.get(pk=value)
def get_prep_value(self, value):
if isinstance(value, self.model_on_other_db):
value = value.pk
return super(ForeignKeyAcrossDb, self).get_prep_value(value)
def get_prep_lookup(self, lookup_type, value):
# TODO: this db lookup is duplicated in to_python()
if not isinstance(value, self.model_on_other_db):
value = self.model_on_other_db._default_manager.get(pk=value)
return super(ForeignKeyAcrossDb, self).get_prep_lookup(lookup_type, value)
from django.db import models
from django.db import router
from django.db.models.query import QuerySet
class ReverseSingleRelatedObjectDescriptor(object):
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# a single "remote" value, on the class that defines the related field.
# In the example "choice.poll", the poll attribute is a
# ReverseSingleRelatedObjectDescriptor instance.
def __init__(self, field_with_rel):
self.field = field_with_rel
def __get__(self, instance, instance_type=None):
if instance is None:
return self
cache_name = self.field.get_cache_name()
try:
return getattr(instance, cache_name)
except AttributeError:
val = getattr(instance, self.field.attname)
if val is None:
# If NULL is an allowed value, return it.
if self.field.null:
return None
raise self.field.rel.to.DoesNotExist
other_field = self.field.rel.get_related_field()
if other_field.rel:
params = {'%s__pk' % self.field.rel.field_name: val}
else:
params = {'%s__exact' % self.field.rel.field_name: val}
# If the related manager indicates that it should be used for
# related fields, respect that.
rel_mgr = self.field.rel.to._default_manager
db = router.db_for_read(self.field.rel.to, instance=instance)
if getattr(rel_mgr, 'forced_using', False):
db = rel_mgr.forced_using
rel_obj = rel_mgr.using(db).get(**params)
elif getattr(rel_mgr, 'use_for_related_fields', False):
rel_obj = rel_mgr.using(db).get(**params)
else:
rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
setattr(instance, cache_name, rel_obj)
return rel_obj
def __set__(self, instance, value):
raise NotImplementedError()
class ForeignKeyAcrossDb(models.ForeignKey):
def contribute_to_class(self, cls, name):
models.ForeignKey.contribute_to_class(self, cls, name)
setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self))
if isinstance(self.rel.to, basestring):
target = self.rel.to
else:
target = self.rel.to._meta.db_table
cls._meta.duplicate_targets[self.column] = (target, "o2m")
def validate(self, value, model_instance):
pass
from django.core import exceptions
from django.db import models
from django.db import router
class ForeignKeyAcrossDb(models.ForeignKey):
def validate(self, value, model_instance):
if self.rel.parent_link:
return
models.Field.validate(self, value, model_instance)
if value is None:
return
using = router.db_for_read(self.rel.to, instance=model_instance) # is this more correct than Django's 1.2.5 version?
qs = self.rel.to._default_manager.using(using).filter(
**{self.rel.field_name: value}
)
qs = qs.complex_filter(self.rel.limit_choices_to)
if not qs.exists():
raise exceptions.ValidationError(self.error_messages['invalid'] % {
'model': self.rel.to._meta.verbose_name, 'pk': value})
水果应用程序/models.py:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
class Article(models.Model):
fruit = models.ForeignKey('fruit_app.Fruit')
intro = models.TextField()
from django.db import models
class FruitManager(models.Manager):
forced_using = 'default'
class Fruit(models.Model):
name = models.CharField(max_length=20)
objects = FruitManager()
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
新闻\u app/models.py:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
class Article(models.Model):
fruit = models.ForeignKey('fruit_app.Fruit')
intro = models.TextField()
from django.db import models
class FruitManager(models.Manager):
forced_using = 'default'
class Fruit(models.Model):
name = models.CharField(max_length=20)
objects = FruitManager()
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
方法2a:为fruit_应用程序添加路由器
此解决方案为水果应用程序
使用额外的路由器。此解决方案不需要对方法2中要求的ForeignKey
进行修改。在中查看Django的默认路由行为后,我们发现,尽管我们预期默认情况下fruit\u app
位于'default'
数据库中,但传递给db\u进行外键查找的instance
提示将其置于'news\u db'
数据库中。我们添加了第二个路由器,以确保始终从'default'
数据库中读取水果应用程序
模型。ForeignKey
子类仅用于“修复”ForeignKey.validate()
方法。(如果Django希望跨数据库支持外键,我会说这是一个Django bug。)
代码位于github上的以下位置:
路由器.py:
class NewsRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'news_app' or obj2._meta.app_label == 'news_app':
return True
return None
def allow_syncdb(self, db, model):
if db == 'news_db':
return model._meta.app_label == 'news_app'
elif model._meta.app_label == 'news_app':
return False
return None
class NewsRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'news_app':
return 'news_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'news_app' or obj2._meta.app_label == 'news_app':
return True
return None
def allow_syncdb(self, db, model):
if db == 'news_db':
return model._meta.app_label == 'news_app'
elif model._meta.app_label == 'news_app':
return False
return None
class FruitRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'fruit_app':
return 'default'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'fruit_app':
return 'default'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'fruit_app' or obj2._meta.app_label == 'fruit_app':
return True
return None
def allow_syncdb(self, db, model):
if db == 'default':
return model._meta.app_label == 'fruit_app'
elif model._meta.app_label == 'fruit_app':
return False
return None
水果应用程序/models.py:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
class Article(models.Model):
fruit = models.ForeignKey('fruit_app.Fruit')
intro = models.TextField()
from django.db import models
class FruitManager(models.Manager):
forced_using = 'default'
class Fruit(models.Model):
name = models.CharField(max_length=20)
objects = FruitManager()
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
新闻\u app/models.py:
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
class Article(models.Model):
fruit = models.ForeignKey('fruit_app.Fruit')
intro = models.TextField()
from django.db import models
class FruitManager(models.Manager):
forced_using = 'default'
class Fruit(models.Model):
name = models.CharField(max_length=20)
objects = FruitManager()
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=20)
from django.db import models
from foreign_key_across_db_testproject.fields import ForeignKeyAcrossDb
from foreign_key_across_db_testproject.fruit_app.models import Fruit
class Article(models.Model):
fruit = ForeignKeyAcrossDb(Fruit)
intro = models.TextField()
def fruit_name(self):
return self.fruit.name
fields.py:
from django.db import models
class ForeignKeyAcrossDb(models.IntegerField):
'''
Exists because foreign keys do not work across databases
'''
def __init__(self, model_on_other_db, **kwargs):
self.model_on_other_db = model_on_other_db
super(ForeignKeyAcrossDb, self).__init__(**kwargs)
def to_python(self, value):
# TODO: this db lookup is duplicated in get_prep_lookup()
if isinstance(value, self.model_on_other_db):
return value
else:
return self.model_on_other_db._default_manager.get(pk=value)
def get_prep_value(self, value):
if isinstance(value, self.model_on_other_db):
value = value.pk
return super(ForeignKeyAcrossDb, self).get_prep_value(value)
def get_prep_lookup(self, lookup_type, value):
# TODO: this db lookup is duplicated in to_python()
if not isinstance(value, self.model_on_other_db):
value = self.model_on_other_db._default_manager.get(pk=value)
return super(ForeignKeyAcrossDb, self).get_prep_lookup(lookup_type, value)
from django.db import models
from django.db import router
from django.db.models.query import QuerySet
class ReverseSingleRelatedObjectDescriptor(object):
# This class provides the functionality that makes the related-object
# managers available as attributes on a model class, for fields that have
# a single "remote" value, on the class that defines the related field.
# In the example "choice.poll", the poll attribute is a
# ReverseSingleRelatedObjectDescriptor instance.
def __init__(self, field_with_rel):
self.field = field_with_rel
def __get__(self, instance, instance_type=None):
if instance is None:
return self
cache_name = self.field.get_cache_name()
try:
return getattr(instance, cache_name)
except AttributeError:
val = getattr(instance, self.field.attname)
if val is None:
# If NULL is an allowed value, return it.
if self.field.null:
return None
raise self.field.rel.to.DoesNotExist
other_field = self.field.rel.get_related_field()
if other_field.rel:
params = {'%s__pk' % self.field.rel.field_name: val}
else:
params = {'%s__exact' % self.field.rel.field_name: val}
# If the related manager indicates that it should be used for
# related fields, respect that.
rel_mgr = self.field.rel.to._default_manager
db = router.db_for_read(self.field.rel.to, instance=instance)
if getattr(rel_mgr, 'forced_using', False):
db = rel_mgr.forced_using
rel_obj = rel_mgr.using(db).get(**params)
elif getattr(rel_mgr, 'use_for_related_fields', False):
rel_obj = rel_mgr.using(db).get(**params)
else:
rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
setattr(instance, cache_name, rel_obj)
return rel_obj
def __set__(self, instance, value):
raise NotImplementedError()
class ForeignKeyAcrossDb(models.ForeignKey):
def contribute_to_class(self, cls, name):
models.ForeignKey.contribute_to_class(self, cls, name)
setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self))
if isinstance(self.rel.to, basestring):
target = self.rel.to
else:
target = self.rel.to._meta.db_table
cls._meta.duplicate_targets[self.column] = (target, "o2m")
def validate(self, value, model_instance):
pass
from django.core import exceptions
from django.db import models
from django.db import router
class ForeignKeyAcrossDb(models.ForeignKey):
def validate(self, value, model_instance):
if self.rel.parent_link:
return
models.Field.validate(self, value, model_instance)
if value is None:
return
using = router.db_for_read(self.rel.to, instance=model_instance) # is this more correct than Django's 1.2.5 version?
qs = self.rel.to._default_manager.using(using).filter(
**{self.rel.field_name: value}
)
qs = qs.complex_filter(self.rel.limit_choices_to)
if not qs.exists():
raise exceptions.ValidationError(self.error_messages['invalid'] % {
'model': self.rel.to._meta.verbose_name, 'pk': value})
补充资料
- django用户列表中包含大量信息的线程:
- 多数据库文档的修订历史记录:
更新
在进一步调整路由器之后,我们实现了最后一种方法。整个实施过程相当痛苦,这让我们认为我们一定是做错了。在TODO列表中,正在为此编写单元测试。您可以在数据库中创建一个包含跨数据库查询的视图,然后在单独的文件中定义该视图的模型,以保持syncdb正常工作
快乐编程。:) 我知道Djano nosql支持按键之类的功能,尽管它有点神奇。也许其中的一些会有所帮助
根据描述:
“您可以告诉dbindexer哪些模型和字段应该支持这些查询,它将负责为您维护所需的索引。”
-Kerry至于ForeignKeyCrossDB
部分,你能不能在\uuuu init\uuuuu
中对你的课程做一些调整?检查相应字段是否为Integer
如果不是,请从数据库中加载该字段,或执行其他所需操作。Python\uuuu class\uuuuuuuu可以在运行时进行更改,而不会有太多问题。外键字段意味着您可以
-通过加入ie FROUT\u name查询关系
-检查引用完整性
-确保删除时的引用完整性
-管理员原始id查找功能
-(还有一些…)
第一个用例总是有问题的。
可能代码库中还有其他一些外键特殊情况也不起作用
我运行一个相当大的django站点,我们目前使用的是一个普通的integerfield。
目前,我认为对integerfield进行子类化并将id添加到对象转换将是最简单的(在1.2中,需要修补一些django,希望现在有所改进)
我会告诉你我们找到了什么解决方案。在打破我的脑袋几天后,我设法在同一家银行拿到了我的外键
可以制作更改表单以在其他银行中查找外键
首先,在函数__; 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()
从视图调用窗体
app.view.py
form = forms.FormNewHelpDesk(request.POST or None, using=banco)
现在,源代码DJANGO中的更改
只有ForeignKey、ManyToManyField和OneToOneField类型的字段才能使用“using”,因此添加了一个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)
更改跟随文件
django.db.models.base.py
改变
为了
就绪:D遇到了一个类似的问题,即需要跨多(5)个数据库引用(大部分)静态数据。对ReversedSingleRelatedObjectDescriptor进行了轻微更新,以允许设置相关模型。它不实现atm的反向关系
class ReverseSingleRelatedObjectDescriptor(object):
"""
This class provides the functionality that makes the related-object managers available as attributes on a model
class, for fields that have a single "remote" value, on the class that defines the related field. Used with
LinkedField.
"""
def __init__(self, field_with_rel):
self.field = field_with_rel
self.cache_name = self.field.get_cache_name()
def __get__(self, instance, instance_type=None):
if instance is None:
return self
try:
return getattr(instance, self.cache_name)
except AttributeError:
val = getattr(instance, self.field.attname)
if val is None:
# If NULL is an allowed value, return it
if self.field.null:
return None
raise self.field.rel.to.DoesNotExist
other_field = self.field.rel.get_related_field()
if other_field.rel:
params = {'%s__pk' % self.field.rel.field_name: val}
else:
params = {'%s__exact' % self.field.rel.field_name: val}
# If the related manager indicates that it should be used for related fields, respect that.
rel_mgr = self.field.rel.to._default_manager
db = router.db_for_read(self.field.rel.to, instance=instance)
if getattr(rel_mgr, 'forced_using', False):
db = rel_mgr.forced_using
rel_obj = rel_mgr.using(db).get(**params)
elif getattr(rel_mgr, 'use_for_related_fields', False):
rel_obj = rel_mgr.using(db).get(**params)
else:
rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
setattr(instance, self.cache_name, rel_obj)
return rel_obj
def __set__(self, instance, value):
if instance is None:
raise AttributeError("%s must be accessed via instance" % self.field.name)
# If null=True, we can assign null here, but otherwise the value needs to be an instance of the related class.
if value is None and self.field.null is False:
raise ValueError('Cannot assign None: "%s.%s" does not allow null values.' %
(instance._meta.object_name, self.field.names))
elif value is not None and not isinstance(value, self.field.rel.to):
raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' %
(value, instance._meta.object_name, self.field.name, self.field.rel.to._meta.object_name))
elif value is not None:
# Only check the instance state db, LinkedField implies that the value is on a different database
if instance._state.db is None:
instance._state.db = router.db_for_write(instance.__class__, instance=value)
# Is not used by OneToOneField, no extra measures to take here
# Set the value of the related field
try:
val = getattr(value, self.field.rel.get_related_field().attname)
except AttributeError:
val = None
setattr(instance, self.field.attname, val)
# Since we already know what the related object is, seed the related object caches now, too. This avoids another
# db hit if you get the object you just set
setattr(instance, self.cache_name, value)
if value is not None and not self.field.rel.multiple:
setattr(value, self.field.related.get_cache_name(), instance)
及
受到@Frans评论的启发。我的解决方法是在业务层完成这项工作。在这个问题给出的例子中。我将在文章
中创建一个整型字段
,作为“不在数据层中进行完整性检查”
然后遵循应用程序代码(业务层)中的引用关系。以Django admin为例,为了在文章的添加页面中将水果显示为选项,您可以手动填充水果的选项列表
# admin.py in App article
class ArticleAdmin(admin.ModelAdmin):
class ArticleForm(forms.ModelForm):
fields = ['fruit', 'intro']
# populate choices for fruit
choices = [(obj.id, obj.name) for obj in Fruit.objects.all()]
widgets = {
'fruit': forms.Select(choices=choices)}
form = ArticleForm
list_diaplay = ['fruit', 'intro']
当然,您可能需要处理表单字段验证(完整性检查)。我有一个