Django代理模型和ForeignKey

时间:2010-10-08 15:10:13

标签: python django django-models

如何使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

但看起来很糟糕......

7 个答案:

答案 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)

Joseph Spiros对我的一个问题的回答可能会对你有所帮助:

Django Inheritance and Permalinks

我不确定它如何与代理模型一起使用。

答案 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),然后在以后的所有时间返回。