有没有办法确定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!
但这似乎不起作用。
答案 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
异常。
另请参阅我的回答here和django-model-utils中的InheritanceManager
,以获得更优雅/长期的解决方案。
我们正在研究如何在Django的核心中使这更容易。
答案 3 :(得分:2)
要解决此问题,请考虑使用django-polymorphic。它支持自动向下转换继承的模型,与ForeignKeys / ManyToMany字段一起使用,并在管理员中集成。