【问题标题】:How do I find the "concrete class" of a django model baseclass如何找到 django 模型基类的“具体类”
【发布时间】:2010-09-25 20:09:18
【问题描述】:

在使用模型继承时,我正在尝试查找 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()

我想在 basemethod 中确定对象是 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

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

【问题讨论】:

    标签: python django inheritance django-models


    【解决方案1】:

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

    这个snippet 展示了一种将 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() 来确定类型。

    Here 是另一个添加自定义管理器的 sn-p。

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

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

    【讨论】:

    • 我不知道是不是因为我在 django 1.4 上,但这对我不起作用,直到我使用 if (not self.content_type_id) 而不是 self.content_type。显然 Django 试图加载 self.content_type,从而导致 DoesNotExist
    • AFAIK 这正是这个包的作用:django-polymorphic.readthedocs.org/en/latest 即使您使用 BaseClass.objects.filter(),它也会返回具体的子类
    • 如何保证子id匹配父id
    【解决方案2】:

    看看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 或更高版本。

    【讨论】:

      【解决方案3】:

      嗯...我的问题是。在一个视图中,我有这个主要模型,比如说“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
      

      【讨论】:

        【解决方案4】:

        what Daniel Naab proposed的略微修改版:

        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
        

        【讨论】:

          【解决方案5】:

          感觉很脆弱,因为它是。 (这是一个在不同背景下的答案的转载。See C++ casting programmatically : can it be done ?

          阅读多态性。几乎所有“动态转换”情况都是难以实现的多态性示例。

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

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

          当您说“我想确定对象的类型是 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.
          

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

          【讨论】:

          • 但他没有对子模型实例的引用。 Base.objects 管理器只返回 Base 的实例。
          • @Daniel:通常我们处理 Child_1.objects.all() 和 Child_2.objects.all();几乎没有要求将两个子亚型结合起来。
          • 一般来说,但这不是问题。
          • @Daniel:这是一个非常常见的 n00b 问题,表明未能利用多态性。消除问题的设计更改可能是更有用的答案。
          • 这种行为与问题完全相反。
          猜你喜欢
          • 2018-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-19
          • 1970-01-01
          • 1970-01-01
          • 2014-01-15
          • 2013-05-04
          相关资源
          最近更新 更多