如何在Django中访问对象的子类而不知道子类名称?
在Django中,当你有一个父类和多个从它继承的子类时,通常你可以通过父类来访问子类,比如用parentclass.childclass1_set或者parentclass.childclass2_set。但是,如果我不知道我想要的具体子类的名字,该怎么办呢?
有没有办法在不知道子类名字的情况下,从父类获取相关的子类对象呢?
7 个回答
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的发展,这个函数的稳定性没有保证。不过它确实能解决问题,如果需要的话,可以随时使用。
在Python中,如果你有一个叫做X的“新式”类,你可以通过调用 X.__subclasses__()
来获取它的直接子类,这个方法会返回一个类对象的列表。如果你想要获取更深层次的子类,你还需要对每个直接子类调用 __subclasses__
,以此类推。如果你需要在Python中有效地做到这一点,可以随时问我!
一旦你找到了一个感兴趣的子类(也许是所有子类,如果你想要所有子类的实例等),你可以使用 getattr(parentclass,'%s_set' % childclass.__name__)
来帮助你(如果子类的名字是 'foo'
,这就像访问 parentclass.foo_set
一样,没什么不同)。如果你需要进一步的解释或例子,随时问我!
(更新: 对于Django 1.2及更新版本,它可以在反向的OneToOneField关系中跟踪select_related查询(因此也可以处理继承层级),现在有一种更好的方法,不需要在父模型上添加real_type
字段。这个方法可以在InheritanceManager中找到,属于django-model-utils项目。)
通常的做法是在父模型上添加一个指向ContentType的外键,这样可以存储正确的“叶子”类的内容类型。如果没有这个,你可能需要对子表进行很多查询,才能找到实例,这取决于你的继承树有多大。以下是我在一个项目中是如何做的:
from django.contrib.contenttypes.models import ContentType
from django.db import models
class InheritanceCastModel(models.Model):
"""
An abstract base class that provides a ``real_type`` FK to ContentType.
For use in trees of inherited models, to be able to downcast
parent instances to their child types.
"""
real_type = models.ForeignKey(ContentType, editable=False)
def save(self, *args, **kwargs):
if self._state.adding:
self.real_type = self._get_real_type()
super(InheritanceCastModel, self).save(*args, **kwargs)
def _get_real_type(self):
return ContentType.objects.get_for_model(type(self))
def cast(self):
return self.real_type.get_object_for_this_type(pk=self.pk)
class Meta:
abstract = True
这个实现是作为一个抽象基类,以便可以重复使用;你也可以把这些方法和外键直接放到你特定继承层级的父类上。
如果你不能修改父模型,这个解决方案就不适用了。在这种情况下,你基本上只能手动检查所有子类。