在基类查询后确定Django模型实例类型

时间:2011-03-07 21:36:14

标签: django inheritance django-models django-inheritance

有没有办法确定Django数据库对象的'真正'类是什么,在从基类的查询返回后?

例如,如果我有这些模型......

class Animal(models.Model):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

创建这些实例......

Person(name='Dave').save()
Dog(name='Mr. Rufflesworth').save()

如果我执行Animal.objects.all()之类的查询,我最终会得到两个Animal实例,而不是Person的实例和Dog的实例。有没有办法确定哪个实例属于哪种类型?


仅供参考:我已经尝试过这样做了......

isinstance(Animal.objects.get(name='Dave'),Person) # <-- Returns false!

但这似乎不起作用。

4 个答案:

答案 0 :(得分:17)

我们实现了自己的cast()函数,该函数运行良好(没有ContentType的):

class Base(models.Model):
    """
    If your class needs the basics, like created date, modified date etc, then
    inherit from this Base class.
    """
    created = models.DateTimeField(_('Created'), auto_now_add=True)
    modified = models.DateTimeField(_('Modified'), auto_now=True)

    class Meta:
        abstract = True

    def __str__(self):
        return '%s [%s]' % (self.__class__.__name__, self.id)

    def get_class_name(self):
        return str(self.__class__.__name__).lower()

    def to_json(self, include_related=True):
        return {
            'id': self.id,
            'created': self.created.isoformat(),
            'modified': self.modified.isoformat(),
            'class_name': self.__class__.__name__
        }

    def cast(self):
        """
        This method is quite handy, it converts "self" into its correct child class. For example:

        .. code-block:: python

           class Fruit(models.Model):
               name = models.CharField()

           class Apple(Fruit):
               pass

           fruit = Fruit.objects.get(name='Granny Smith')
           apple = fruit.cast()

        :return self: A casted child class of self
        """
        for name in dir(self):
            try:
                attr = getattr(self, name)
                if isinstance(attr, self.__class__):
                    return attr
            except:
                pass
        return self

答案 1 :(得分:10)

我过去遇到过类似的问题,最终找到了一个满意的解决方案,感谢this answer

通过实现存储 real 类并由父类继承的抽象类,可以将每个父类实例强制转换为实际类型。 (该答案中使用的抽象类现已在django-model-utils中提供。)

例如,一旦定义了抽象类(或者如果你有django-model-utils),你就可以这样做:

class Animal(InheritanceCastModel):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

使用它是微不足道的:

>>> from zoo.models import Animal, Person, Dog
>>> Animal(name='Malcolm').save()
>>> Person(name='Dave').save()
>>> Dog(name='Mr. Rufflesworth').save()
>>> for obj in Animal.objects.all():
...     print obj.name, type(obj.cast())
...
Malcolm <class 'zoo.models.Animal'>
Dave <class 'zoo.models.Person'>
Mr. Rufflesworth <class 'zoo.models.Dog'>

答案 2 :(得分:9)

是的,可以这样做 - 您只需要查询自动反向OneToOneField关系。 E.g:

a = Animal.objects.select_related('person', 'dog')[0]
a = a.person or a.dog or a # whichever is not None
print a
print isinstance(a, Person)

这里使用select_related允许在单个查询中完成此操作,而不必在访问子类属性/关系时测试DoesNotExist异常。

另请参阅我的回答heredjango-model-utils中的InheritanceManager,以获得更优雅/长期的解决方案。

我们正在研究如何在Django的核心中使这更容易。

答案 3 :(得分:2)

要解决此问题,请考虑使用django-polymorphic。它支持自动向下转换继承的模型,与ForeignKeys / ManyToMany字段一起使用,并在管理员中集成。