13

使用模型继承时,我试图找到 django-model 对象的实际类。

一些描述问题的代码:

class Base(models.model):
    def basemethod(self):
        ...

class Child_1(Base):
    pass

class Child_2(Base):
    pass

如果我创建两个子类的各种对象并创建一个包含它们的查询集:

Child_1().save()
Child_2().save()
(o1, o2) = Base.objects.all()

我想确定对象是基类方法中的 Child_1 还是 Child_2 类型,我可以通过 o1.child_1 和 o2.child_2 获取子对象,但这会重新获得有关基类中子类的知识。

我想出了以下代码:

def concrete_instance(self):
    instance = None
    for subclass in self._meta.get_all_related_objects():
        acc_name = subclass.get_accessor_name()
        try:
            instance = self.__getattribute__(acc_name)
            return instance
        except Exception, e:
            pass

但它感觉很脆弱,我不确定如果我继承更多级别会发生什么。

4

5 回答 5

14

Django 使用父模型表和子模型表之间的 OneToOneField 实现模型继承。当你这样做时Base.object.all(),Django 只查询基表,因此无法知道子表是什么。因此,不幸的是,如果没有额外的查询,就不可能直接进入子模型实例。

代码段显示了将 ContentType 字段添加到基本模型的常用方法:

from django.contrib.contenttypes.models import ContentType

class Base(models.Model):
    content_type = models.ForeignKey(ContentType,editable=False,null=True)

    def save(self):
        if(not self.content_type):
            self.content_type = ContentType.objects.get_for_model(self.__class__)
        self.save_base()

    def as_leaf_class(self):
        content_type = self.content_type
        model = content_type.model_class()
        if(model == Base):
            return self
        return model.objects.get(id=self.id)

然后你可以说if Base.content_type.model_class()确定类型。

是另一个将自定义管理器添加到组合中的片段。

如您所见,这两种解决方案都有可能非常昂贵。如果您有大量实例,则使用 as_leaf_class() 方法将需要对每个项目进行一次查询。

相反,如果您有一组已知的子模型,只需分别查询每个模型并将实例聚合到一个列表中。

于 2008-12-08T13:05:59.620 回答
5

看看django-model-utils中的 InheritanceManager - 将它附加到模型中会为您提供具体的子类(至少在第一级):

from model_utils.managers import InheritanceManager

class Base(models.Model):
    objects = InheritanceManager()

# ...

Base.objects.all().select_subclasses() # returns instances of child classes

model-utils 需要 Django 1.2 或更高版本。

于 2011-12-12T17:59:21.447 回答
0

嗯...我的问题是。在一个视图中,我有这个主要模型,比如说“Big_Model”,还有一些与“Big_Model”相关的“Small_Model”。因此,当我想检索与“Big_Model”的某个实例相关的所有“Small_Model”时,我做了 **_set.all() 的事情。但关键是 Small_Model 有子类,我想在 views.py 中获取与每个 Small_Model 实例相关的子类。我的诀窍是在模型 Small_Model 中定义布尔方法,例如 is_child_1() 和 is_child_2()。当它为真时,您应用实际的子指针而不是 Small_Model 指针。

好的......这还不够清楚,我仍然没有太多时间写一个好的例子,所以我将在这里复制粘贴我的案例:

class Cache(models.Model):
  valor = models.DecimalField(max_digits=9, decimal_places=2, blank= True, null= True)
  evento=models.ForeignKey(Evento)
  def __unicode__(self):
    return u'%s: %s' % (self.evento, self.valor)
  class Meta:
    verbose_name='Cachê'
    verbose_name_plural='Cachês'
  def is_cb(self):
    try:
      self.cache_bilheteria
      return True
    except self.DoesNotExist:
      return False
  def is_co(self):
    try:
      self.cache_outro
      return True
    except self.DoesNotExist:
      return False
于 2010-05-29T19:19:48.387 回答
0

Daniel Naab 提议的略微修改版本:

from django.contrib.contenttypes.models import ContentType
from django.db import models

def ParentClass(models.Model):
    superclass = models.CharField(max_length = 255, blank = True)

    def save(self, *args, **kwargs):
        if not self.superclass:
            self.superclass = ContentType.objects.get_for_model(self.__class__)

        super(ParentClass, self).save(*args, **kwargs)

    def getChild(self):
        s = getattr(self, self.superclass)
        if hasattr(s, 'pk'):
            return s
        else:
            return None

class Child1(ParentClass):
    pass

class Child2(ParentClass):
    pass
于 2013-07-03T08:56:44.473 回答
-2

感觉很脆弱,因为它是。(这是在不同上下文中的答案的重印。 请参阅以编程方式进行 C++ 转换:可以完成吗?

阅读多态性。几乎每一个“动态转换”情况都是多态性难以实现的例子。

无论您在动态演员阵容中做出的决定都已经做出。只需将实际工作委托给子类即可。

您遗漏了示例中最重要的部分。有用的、多态的工作。

当您说“我想确定对象的类型是 Child_1 还是 Child_2 ...”时,您忽略了“所以我可以使对象aMethod()以每个子类独有的方式执行”。该方法是有用的工作,它应该只是两个子类的方法。

class Base(models.model):
    def aMethod(self):
        # base class implementation.

class Child_1(Base):
    def aMethod(self):
        # Child_1 override of base class behavior.

class Child_2(Base):
    def aMethod(self):
        supert( Child_2, self ).aMethod() # Invoke the base class version
        # Child_2 extension to base class behavior.

相同的方法,多种实现。永远不需要“运行时类型识别”或确定具体类。

于 2008-12-08T11:23:59.090 回答