在Django中,当您有一个父类和从其继承的多个子类时,通常可以通过parentclass.childclass1_set或parentclass.childclass2_set访问一个子类,但是如果我不知道我想要的特定子类的名称怎么办?
有没有一种方法可以在不知道子类名称的情况下沿parent-> child方向获取相关对象?
在Django中,当您有一个父类和从其继承的多个子类时,通常可以通过parentclass.childclass1_set或parentclass.childclass2_set访问一个子类,但是如果我不知道我想要的特定子类的名称怎么办?
有没有一种方法可以在不知道子类名称的情况下沿parent-> child方向获取相关对象?
Answers:
(更新:对于Django 1.2及更高版本,它可以在反向的OneToOneField关系中遵循select_related查询(从而实现向下的继承层次结构),有一种更好的技术,它不需要real_type
在父模型上添加字段。它可以在InheritanceManager中使用django-model-utils项目。)
这样做的通常方法是将ParentKey添加到Parent模型的ContentType上,该模型存储正确的“ leaf”类的内容类型。否则,您可能必须对子表进行大量查询才能找到实例,具体取决于继承树的大小。这是我在一个项目中做的事情:
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
它被实现为抽象基类以使其可重用。您还可以将这些方法和FK直接放在特定继承层次结构中的父类上。
如果您无法修改父模型,则此解决方案将不起作用。在这种情况下,您几乎无法手动检查所有子类。
self.child_object()
使用real_type
字段(在上面的代码中作为cast()
方法),并且只需要对一个实例进行一次查询。但是,如果您有一个充满实例的查询集,那么它将成为N个查询。在单个查询中获取整个对象查询集的子类数据的唯一方法是使用InheritanceManager所做的联接。
在Python中,给定(“新样式”)类X,可以使用来获得其(直接)子类X.__subclasses__()
,该子类返回类对象的列表。(如果需要“其他__subclasses__
子类”,则还必须调用每个直接子类,等等,如果需要帮助,以帮助您有效地在Python中做到这一点,请问!)。
一旦以某种方式确定了感兴趣的子类(可能是所有子类,如果要所有子类的实例,等等),就getattr(parentclass,'%s_set' % childclass.__name__)
应该有所帮助(如果子类的名称是'foo'
,这就像访问parentclass.foo_set
-不多也不少) )。同样,如果您需要澄清或示例,请询问!
您可以为此使用django-polymorphic。
它允许将派生类自动转换回其实际类型。它还提供Django管理员支持,更有效的SQL查询处理以及代理模型,内联和表单集支持。
基本原理似乎已被多次改造(包括Wagtail的方法.specific
,或本文中概述的示例)。但是,要确保不会导致N-query问题,或者与管理员,表单集/内联或第三方应用很好地集成,需要花费更多的精力。
这是我的解决方案,它再次使用它,_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实例的所有字段来实现此目的。从您的示例看来,您正在谈论的子类不是子类。对?
可以在此博客文章中找到使用代理的替代方法。像其他解决方案一样,它也有其优点和缺点,这些优点和缺点很好地放在了帖子的结尾。