如何使entry.category成为CategoryProxy的实例?有关详细信息,请参阅代码:
class Category(models.Model): pass
class Entry(models.Model):
category = models.ForeignKey(Category)
class EntryProxy(Entry):
class Meta:
proxy = True
class CategoryProxy(Category):
class Meta:
proxy = True
entry = EntryProxy.objects.get(pk=1)
entry.category # !!! I want CategoryProxy instance here
从类别转换为CategoryProxy也可以,但是我不熟悉ORM内部以正确复制内部状态...
EDIT。 原因:我将方法添加到CategoryProxy并想要使用他:
EntryProxy.objects.get(pk=1).category.method_at_category_proxy()
编辑2。 目前我实现了这样:
EntryProxy._meta.get_field_by_name('category')[0].rel.to = CategoryProxy
但看起来很糟糕......
答案 0 :(得分:12)
要从模型类切换到代理类而不点击数据库:
class EntryProxy(Entry):
@property
def category(self):
new_inst = EntryProxy()
new_inst.__dict__ = super(EntryProxy, self).category.__dict__
return new_inst
编辑:上面的代码段似乎无法在django 1.4上运行。
从django 1.4开始,我手动获取所有值字段:
class EntryProxy(Entry):
@property
def category(self):
category = super(EntryProxy, self).category
new_inst = EntryProxy()
for attr in [f.attname for f in category.__class__._meta.fields] + ['_state']:
setattr(new_inst, attr, getattr(category, attr))
return new_inst
在不命中数据库的情况下从查询集切换到子代理类:
class CategoryProxy(Category):
@property
def entry_set(self):
qs = super(CategoryProxy, self).entry_set
qs.model = EntryProxy
return qs
答案 1 :(得分:6)
这是一个开放的Django问题:#10961 (Allow users to override forward and reverse relationships on proxy models with ForeignKey fields)
您可以在定义代理模型后重置相关字段来解决此问题:
EntryProxy.add_to_class('category', CategoryProxy)
答案 2 :(得分:1)
这个问题已经有了一个公认的答案,但是想把它发布给任何可能来搜索的人。
您可以使用新字段在运行时修补模型,以便关系按预期工作。这里可以看到一个完整的例子 - https://gist.github.com/carymrobbins/8721082
from django.db.models.fields.related import ReverseSingleRelatedObjectDescriptor
def override_model_field(model, field, field_name, column_name):
"""Force override a field in a Django Model.
Usage: override_model_field(
MyModel, models.ForeignKey(OtherModel), 'other', 'other_id')
:type model: django.db.models.base.ModelBase
:type field: django.db.models.fields.Field
:type field_name: basestring
:type column_name: basestring
"""
field.name = field_name
field.attname = column_name
for i, f in enumerate(model._meta.fields):
if f.name == field_name:
model._meta.fields[i] = field
break
else:
raise TypeError('Model {!r} does not have a field {!r}.'
.format(model, field_name))
model.add_to_class(field_name,
ReverseSingleRelatedObjectDescriptor(field))
答案 3 :(得分:0)
答案 4 :(得分:0)
当前的解决方案(包括已接受的解决方案)都无法在Django 2.0中使用。
基于Matt Schinckel的work on overriding proxy model relations,这里是solution that will work with Django 2.0 and 2.1。
答案 5 :(得分:-1)
在EntryProxy上定义属性category
,通过其ID查找CategoryProxy:
class EntryProxy(Entry):
@property
def category(self):
cid = super(EntryProxy, self).category.id
return CategoryProxy.objects.get(id=cid)
class Meta:
proxy = True
答案 6 :(得分:-1)
稍微调整Bernd Petersohn的答案,然后我们有:
class EntryProxy(Entry):
@property
def category(self):
return CategoryProxy.objects.get(id=self.category_id)
这对数据库来说应该更经济。为了进一步改进,您可以在第一次调用方法时设置私有属性(self._category
),然后在以后的所有时间返回。