Is there a way to determine what the 'real' class of a Django database object is, after it has been returned from a query for on a base class?
For instance, if I have these models...
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)
And create these instances...
Person(name='Dave').save()
Dog(name='Mr. Rufflesworth').save()
If I do a query like Animal.objects.all()
, I end up with two Animal
instances, not an instance of Person
and an instance of Dog
. Is there any way to determine which instance is of which type?
FYI: I already tried doing this...
isinstance(Animal.objects.get(name='Dave'),Person) # <-- Returns false!
But that doesn't seem to work.
str function in a django model returns a string that is exactly rendered as the display name of instances for that model. # Create your models here. This will display the objects as something always in the admin interface.
def str(self): is a python method which is called when we use print/str to convert object into a string.
The basics: Each model is a Python class that subclasses django.db.models.Model . Each attribute of the model represents a database field.
To create a new instance of a model, instantiate it like any other Python class: class Model (**kwargs) The keyword arguments are the names of the fields you've defined on your model. Note that instantiating a model in no way touches your database; for that, you need to save() .
We implemented our own cast() function that works quite well (Without ContentType's):
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
I had a similar problem in the past and eventually found a satisfactory solution thanks to this answer.
By implementing an abstract class that stores the real class and have it inherited by your parent class, once can cast each parent class instance to the actual type. (The abstract class used in that answer is now available in django-model-utils.)
For example, once you have the abstract class defined (or if you have django-model-utils), you can simply do:
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)
Using it is trivial:
>>> 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'>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With