如何将多态项分解成容器上它们各自的相关列表? [英] How can polymorphic items be broken into their respective related lists on a container?

查看:43
本文介绍了如何将多态项分解成容器上它们各自的相关列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我有一组这样的模型:

Say I have a set of models like this:

class Container(models.Model):
    pass

class Parent(models.Model):
    container = models.ForeignKey(Container, related_name='%(class)s_list')

class Child(Parent):
    pass

如果不使 Parent 成为抽象,是否有可能使 Child container 字段具有相关的名称为 child_list Child 的实例放入 Container 模型的 child_list 中?

Without making Parent abstract, is it possible to make Child's container field have a related name of child_list instead and all Child's instances would be put in child_list for the Container model?

我尝试将 Child 修改为具有其他字段:

I've tried modifying Child to have an additional field:

class Child(Parent):
    container2 = models.ForeignKey(Container, related_name='%(class)s_list')

然后使两个字段同步".但是,由于这样做的真正目的是使用与继承相关的子类优化许多子类,所以可以优化父类.

And then making the two fields be in sync'. However, since the real goal of this is to optimize a parent class with many inheritance-related children [1], using an aliased Container key doesn't populate the cache correctly when doing a prefetch.

任何解决方案都可能导致列表 child_list 中的 Child 实例进入 sample_list 中的别名.但是,我不打算公开 sample_list ,我很乐意解决别名问题.

Any solution would probably cause an aliasing issue with Child instance in list child_list into sample_list. However, I'm not intending to expose sample_list and I'd be happy to work around aliasing.

推荐答案

我找到了一个错误的答案:

I found a wicked answer:

  • Create a Container pointer in the Parent and the Child and keep them in sync'.
  • Use select_subclasses() from django-model-utils to populate the parent_list with all the different Child objects.
  • wrapper QuerySet's _prefetch_related_objects so that after the original call, the child objects are split into their respective buckets into _prefetched_objects_cache
  • wrapper QuerySet's _clone function in order to re-wraper _prefetch_related_objects() each time the QuerySet is _clone()'d
# get all the subclasses of the `Parent` type
# from http://stackoverflow.com/a/408465/20712
def find_subclasses(module, clazz):
    return [
        cls
            for name, cls in inspect.getmembers(module)
                if inspect.isclass(cls) and issubclass(cls, clazz)
    ]
parent_subclasses = find_subclasses(app.models,Parent)
parent_subclass_2_related_name = {}
for c in parent_subclasses:
    try:
        parent_subclass_2_related_name[c.__name__] = c.container.field.rel.related_name
    except (AttributeError):
        pass

# define the query and selectively wrapper it    
qs = Container.objects.all()\
    .prefetch_related(Prefetch('parent_list',Parent.objects.select_subclasses()))

def attach_clone(obj):
    original_clone = obj._clone
    def new_clone(*args,**kwargs):
        result = original_clone(*args,**kwargs)
        attach_clone(result) # re-wrapper _clone
        old_prefetch = result._prefetch_related_objects
        def new_prefetch(*args):
            old_prefetch()
            for obj in result._result_cache:
                for s in obj._prefetched_objects_cache['parent_list']:
                    obj._prefetched_objects_cache.setdefault(parent_subclass_2_related_name[s.__class__.__name__],[]).append(s)
        result._prefetch_related_objects = new_prefetch
        return result
    obj._clone = new_clone
attach_clone(qs)

return qs

这篇关于如何将多态项分解成容器上它们各自的相关列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆