如何访问在Django对象的子类不知道子类的名字吗?
-
06-09-2019 - |
题
在Django的,当你有一个父类和多个子类继承它,你通常会通过parentclass.childclass1_set或parentclass.childclass2_set,但什么访问一个孩子,如果我不知道具体的子类的名称我想要的吗?
有没有办法让在父母与相关对象>子方向,而不知道子类的名字吗?
解决方案
(的更新强>:对于Django的1.2和更新的,这可以按照跨越反向OneToOneField关系select_related查询(和由此向下继承层次结构),有可用的较好技术,该技术不需要附加real_type
场父模型。它可以作为 InheritanceManager 在 django的模型-utils的项目。)
在通常的方式做到这一点是添加一个ForeignKey到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 not 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
这是这样实现的一个抽象基类,使之可重复使用的;你也可以把这些方法和FK直接到父类中的特定的继承层次。
如果你不能够修改父模型该解决方案将无法工作。在这种情况下,你几乎陷入手动检查所有的子类。
其他提示
在Python中,赋予了(“新式”)类X,你可以得到它(直接)与子类X.__subclasses__()
,返回类对象的列表。 (如果你想“进一步的后裔”,你还必须调用每个直接的子类,等等等等的__subclasses__
- !如果您需要关于如何有效地做到这一点在Python帮助,只问)。
一旦你以某种方式确定了子类的利益(也许所有的人,如果你希望所有的孩子子类等的情况下),getattr(parentclass,'%s_set' % childclass.__name__)
应该帮助(如果该子类的名字是'foo'
,这就像访问parentclass.foo_set
- - 无多,不会少)。同样,如果你需要澄清或实例,请咨询!
卡尔的解决方案是一个很好的一个,这里是做手工,如果有多个相关的子类的一种方式:
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的演变稳定的功能,但它确实特技并且可以在即时使用,如果需要的话。
可以使用 django的多态性获得这一点。
它允许自动转换派生类回到它们的实际类型。它还提供Django管理支持,更高效的SQL查询处理,代理模型,内联和表单集支持。
的基本原理似乎是彻底改造多次(包括鸰的.specific
,或在该交概述的实施例)。它需要更多的努力。然而,以确保它不会导致在N-查询问题,或者很好地与管理,表单集/内联或第三方应用程序集成。
下面是我的解决方案,它再次使用_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
您可以做到这一点寻找所有的父母是django.db.models.fields.related.RelatedManager的实例字段。从你的例子似乎你正在谈论的子类都没有子类。正确?
使用代理的另一种方法可以在此博客中找到帖子。像其他的解决方案,它有它的好处和责任,这是很好的放在后结束。