如何将多态项分解成容器上它们各自的相关列表? [英] How can polymorphic items be broken into their respective related lists on a container?
问题描述
说我有一组这样的模型:
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:
- 在
Parent
和Child
中创建一个Container
指针,并使它们保持同步." - 使用
django-model-utils
,以使用所有不同的Child
对象填充parent_list
. - 包装器
QuerySet
的_prefetch_related_objects
,以便在原始调用之后,将子对象拆分为各自的存储区,分别放入_prefetched_objects_cache
- 包装器
QuerySet
的_clone
函数,以便在每次QuerySet
时重新包装_prefetch_related_objects()
是_clone()
'
- Create a
Container
pointer in theParent
and theChild
and keep them in sync'. - Use
select_subclasses()
fromdjango-model-utils
to populate theparent_list
with all the differentChild
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 theQuerySet
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屋!