11

在从对基类的查询中返回后,有没有办法确定 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

4 回答 4

18

我们实现了自己的 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
于 2012-11-09T10:42:02.220 回答
11

过去我也遇到过类似的问题,最终由于这个答案找到了令人满意的解决方案。

通过实现一个抽象类来存储真实类并让它由你的父类继承,一次可以将每个父类实例转换为实际类型。(该答案中使用的抽象类现在在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'>
于 2011-03-08T10:06:20.480 回答
9

是的,这可以做到——您只需要查询自动反向OneToOneField关系。例如:

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 的核心中使这更容易的方法。

于 2011-03-07T23:24:39.067 回答
2

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

于 2013-05-20T21:41:40.537 回答