通过代理模型父类的继承模型字段过滤代理模型。

0 投票
1 回答
684 浏览
提问于 2025-04-19 04:29

我正在使用 Django 1.6 并且在使用模型继承。标题可能有点让人困惑,这里是解释:

class ParentModel(models.Model)
    class Meta:
        db_table = "parent_model"

    my_field=.....

class ProxyModelOfParentModel(ParentModel)
    class Meta:
        proxy=True

    objects=CustomManager()


class InheritedModel(ParentModel)
    class Meta:
        db_table = "inherited_model"

    my_extra_field=.....

假设这些是我们的模型。当我尝试在父模型上通过 my_extra_field 进行筛选时,我会这样做:

ParentModel.objects.filter(inheritedmodel__my_extra_field='test')

但是,我想在代理模型上进行筛选,像这样:

ProxyModelOfParentModel.objects.filter(inheritedmodel__my_extra_field='test')

当我运行这个时,它无法在代理模型中找到 inheritedmodel 这个字段。这可能也是 Django 的一个 bug,我不太确定。不知道为什么,当我尝试在代理模型上进行筛选时,Django 没有正确构建查询集。

我使用代理模型而不是父模型的原因是,我想在不同的管理界面中使用代理模型。当我在管理界面中给 list_filter 传递这个键时,我遇到了 FieldDoesNotExists 的错误。

有没有办法像我提到的那样进行筛选呢?谢谢!

1 个回答

0

正如我提到的,这个问题是因为Django在初始化代理模型时的实现。我不确定这算不算是个bug,但我需要想办法解决这个问题。我发现问题出在模型的_meta初始化部分。

django.db.models.options.py这个文件中,我注释掉的那部分代码就是导致这个问题的原因。

def _fill_related_objects_cache(self):
    cache = SortedDict()
    parent_list = self.get_parent_list()
    for parent in self.parents:
        for obj, model in parent._meta.get_all_related_objects_with_model(include_hidden=True):
            #THIS PART WAS CAUSING THE PROBLEM
            # if (obj.field.creation_counter < 0 or obj.field.rel.parent_link) and obj.model not in parent_list:
            #     continue
            if not model:
                cache[obj] = parent
            else:
                cache[obj] = model
    # Collect also objects which are in relation to some proxy child/parent of self.
    proxy_cache = cache.copy()
    for klass in get_models(include_auto_created=True, only_installed=False):
        if not klass._meta.swapped:
            for f in klass._meta.local_fields:
                if f.rel and not isinstance(f.rel.to, six.string_types) and f.generate_reverse_relation:
                    if self == f.rel.to._meta:
                        cache[f.related] = None
                        proxy_cache[f.related] = None
                    elif self.concrete_model == f.rel.to._meta.concrete_model:
                        proxy_cache[f.related] = None
    self._related_objects_cache = cache
    self._related_objects_proxy_cache = proxy_cache

我只是重写了我父模型的Options类和元类,而不是直接重写Django本身,像这样:

class CustomProxyModelOptions(Options):
    def _fill_related_objects_cache(self):
        cache = SortedDict()
        parent_list = self.get_parent_list()
        for parent in self.parents:
            for obj, model in parent._meta.get_all_related_objects_with_model(include_hidden=True):
                if not model:
                    cache[obj] = parent
                else:
                    cache[obj] = model
        # Collect also objects which are in relation to some proxy child/parent of self.
        proxy_cache = cache.copy()
        for klass in get_models(include_auto_created=True, only_installed=False):
            if not klass._meta.swapped:
                for f in klass._meta.local_fields:
                    if f.rel and not isinstance(f.rel.to, six.string_types) and f.generate_reverse_relation:
                        if self == f.rel.to._meta:
                            cache[f.related] = None
                            proxy_cache[f.related] = None
                        elif self.concrete_model == f.rel.to._meta.concrete_model:
                            proxy_cache[f.related] = None
        self._related_objects_cache = cache
        self._related_objects_proxy_cache = proxy_cache


class ProxyModelMeta(ModelBase):
    def __new__(cls, *args, **kwargs):
        model = super(ProxyModelMeta, cls).__new__(cls, *args, **kwargs)
        model._meta.__class__ = CustomProxyModelOptions
        return model

class ParentModel(models.Model)
    class Meta:
        db_table = "parent_model"

    my_field=.....

class ProxyModelOfParentModel(ParentModel)
    __metaclass__= ProxyModelMeta
    class Meta:
        proxy=True

    objects=CustomManager()


class InheritedModel(ParentModel)
    class Meta:
        db_table = "inherited_model"

    my_extra_field=.....

现在,我可以进行过滤了;

ProxyModelOfParentModel.objects.filter(inheritedmodel__my_extra_field='test')

撰写回答