代码之家  ›  专栏  ›  技术社区  ›  Ross Rogers

如何将多态项分解到容器上各自的相关列表中?

  •  0
  • Ross Rogers  · 技术社区  · 11 年前

    假设我有一组这样的模型:

    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 而不是全部 小孩 的实例将放入 子列表(_L) 对于 Container 模型

    我尝试过修改 小孩 要有一个附加字段:

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

    然后使两个字段同步。然而,由于这的真正目标是优化父类 许多的 与继承有关的子女 [1] ,使用别名 容器 执行预取时,key无法正确填充缓存。

    任何解决方案都可能导致 小孩 列表中的实例 子列表(_L) 进入 sample_list 。然而,我不打算暴露 示例列表 我很乐意解决混叠问题。

    1 回复  |  直到 9 年前
        1
  •  0
  •   Ross Rogers    11 年前

    我找到了一个邪恶的答案:

    • 创建 Container 中的指针 Parent 以及 Child 并保持同步。
    • 使用 select_subclasses() 从…起 django-model-utils 以填充 parent_list 所有不同的 小孩 物体。
    • 包装,包装 QuerySet 's _prefetch_related_objects 因此,在原始调用之后,子对象被拆分到各自的存储桶中 _prefetched_objects_cache
    • 包装,包装 查询集 _clone 功能,以便重新包装 _prefetch_related_objects() 每次 查询集 _clone() '天

    # 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