在Django中,当你有一个父类和多个从它继承的子类时,你通常会通过parentclass.childclass1_set或parentclass.childclass2_set访问一个子节点,但如果我不知道特定子类的名称该怎么办?我想要吗?
有没有办法在不知道子类名的情况下获取parent->子方向的相关对象?
答案 0 :(得分:80)
(更新:对于Django 1.2和更新版本,它可以跟踪反向OneToOneField关系中的select_related查询(从而延迟继承层次结构),有一种更好的技术可用,不需要添加{{ 1}}父模型上的字段。它在InheritanceManager项目中以django-model-utils形式提供。)
执行此操作的常用方法是在Parent模型上向ContentType添加ForeignKey,该模型存储正确的“leaf”类的内容类型。如果没有这个,您可能必须对子表执行大量查询才能找到实例,具体取决于继承树的大小。以下是我在一个项目中的表现:
real_type
这是作为抽象基类实现的,以使其可重用;您还可以将这些方法和FK直接放在特定继承层次结构中的父类中。
如果您无法修改父模型,则此解决方案将无效。在这种情况下,你几乎无法手动检查所有子类。
答案 1 :(得分:21)
在Python中,给定一个(“new-style”)类X,您可以使用X.__subclasses__()
获取其(直接)子类,它返回一个类对象列表。 (如果你想要“更进一步的后代”,你还需要在每个直接子类等上调用__subclasses__
- 如果你需要有关如何在Python中有效地做到这一点的帮助,请问!)。
一旦你以某种方式确定了感兴趣的子类(可能是所有子类,如果你想要所有子类的实例等),getattr(parentclass,'%s_set' % childclass.__name__)
应该有帮助(如果子类的名称是'foo'
,这就像访问parentclass.foo_set
一样 - 不多也不少。同样,如果您需要澄清或示例,请询问!
答案 2 :(得分:5)
Carl的解决方案很好,如果有多个相关的子类,这是手动执行此操作的一种方法:
def get_children(self):
rel_objs = self._meta.get_all_related_objects()
return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]
它使用了一个_meta之外的函数,这个函数不能保证在django演变时保持稳定,但它可以解决问题,并且可以在需要时即时使用。
答案 3 :(得分:5)
事实证明,我真正需要的是:
Model inheritance with content type and inheritance-aware manager
这对我来说非常合适。不过,感谢其他所有人。我只是阅读了你的答案,我学到了很多东西!
答案 4 :(得分:3)
您可以使用django-polymorphic。
它允许自动将派生类转换回其实际类型。它还提供Django管理支持,更高效的SQL查询处理,代理模型,内联和formset支持。
基本原则似乎多次被重新发明(包括Wagtail的.specific
,或本文中概述的例子)。但是,要确保它不会导致N查询问题,或者与管理员,formsets / inlines或第三方应用程序很好地集成,需要付出更多努力。
答案 5 :(得分:2)
这是我的解决方案,它再次使用_meta
因此无法保证稳定。
class Animal(models.model):
name = models.CharField()
number_legs = models.IntegerField()
...
def get_child_animal(self):
child_animal = None
for r in self._meta.get_all_related_objects():
if r.field.name == 'animal_ptr':
child_animal = getattr(self, r.get_accessor_name())
if not child_animal:
raise Exception("No subclass, you shouldn't create Animals directly")
return child_animal
class Dog(Animal):
...
for a in Animal.objects.all():
a.get_child_animal() # returns the dog (or whatever) instance
答案 6 :(得分:0)
您可以实现此目的,查找作为django.db.models.fields.related.RelatedManager实例的父级中的所有字段。从您的示例看,您所谈论的子类似乎不是子类。正确?
答案 7 :(得分:0)
可以在this blog post中找到使用代理的替代方法。与其他解决方案一样,它有其优点和责任,这些都很好地放在了帖子的最后。