Django代理模型和外键

21 投票
7 回答
11260 浏览
提问于 2025-04-16 05:12

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

把Category转换成CategoryProxy也是可以的,不过我对ORM的内部机制不太熟悉,所以不知道怎么正确地复制内部状态……

编辑: 原因是:我在CategoryProxy里添加了一个方法,想要使用它:

EntryProxy.objects.get(pk=1).category.method_at_category_proxy()

编辑 2: 目前我这样实现的:

EntryProxy._meta.get_field_by_name('category')[0].rel.to = CategoryProxy

但是看起来很糟糕……

7 个回答

2

目前的解决方案(包括被接受的那个)在Django 2.0上都不管用。

基于Matt Schinckel关于重写代理模型关系的研究,这里有一个适用于Django 2.0和2.1的解决方案

9

这是一个关于Django的公开问题:#10961(允许用户在使用ForeignKey字段的代理模型上覆盖正向和反向关系)

你可以通过在定义代理模型后重置相关字段来解决这个问题:

EntryProxy.add_to_class('category', CategoryProxy)
15

要在不访问数据库的情况下,从模型类切换到代理类:

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

撰写回答