如何在不知道子类名称的情况下访问django中对象的子类?

时间:2009-05-30 04:52:56

标签: python django many-to-many

在Django中,当你有一个父类和多个从它继承的子类时,你通常会通过parentclass.childclass1_set或parentclass.childclass2_set访问一个子节点,但如果我不知道特定子类的名称该怎么办?我想要吗?

有没有办法在不知道子类名的情况下获取parent->子方向的相关对象?

8 个答案:

答案 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中找到使用代理的替代方法。与其他解决方案一样,它有其优点和责任,这些都很好地放在了帖子的最后。