Let\'s say I have a parent class (ThingsThatMigrate) and two children (Coconut and Swallow). Now let\'s say I have a ThingsThatMigrate object. How can I determine if it is
On a Django CMS I work with (Merengue http://www.merengueproject.org/), we store the "classname" attribute that stores what is the real class of the object.
In order to get the real instance we used the following method:
def get_real_instance(self):
""" get object child instance """
def get_subclasses(cls):
subclasses = cls.__subclasses__()
result = []
for subclass in subclasses:
if not subclass._meta.abstract:
result.append(subclass)
else:
result += get_subclasses(subclass)
return result
if hasattr(self, '_real_instance'): # try looking in our cache
return self._real_instance
subclasses = get_subclasses(self.__class__)
if not subclasses: # already real_instance
self._real_instance = getattr(self, self.class_name, self)
return self._real_instance
else:
subclasses_names = [cls.__name__.lower() for cls in subclasses]
for subcls_name in subclasses_names:
if hasattr(self, subcls_name):
return getattr(self, subcls_name, self).get_real_instance()
return self
The important thing of this function is that it keeps in mind if the class is abstract or not, wich change the logic a little bit.
As DrMeer suggested, I highly recommend django-model-utils (hosted on bitbucket now though). I'm not sure it's convincing enough though.
Let a code example prove it:
>>> ThingsThatMigrate.objects.all().select_subclasses()
Coconut, Coconut, Swallow, Coconut, ThingsThatMigrate
It takes one line, objects = InheritanceManager()
in your parent model.
Concrete or abstract inheritance? If concrete:
>>> things = ThingsThatMigrate.objects.all().select_related('coconut', 'swallow')
>>> for thing in things:
... thing = thing.coconut or thing.swallow or thing
... print thing
This can be automated using django-model-utils InheritanceManager
(then you don't need to worry about select_related
or manually listing all possible subclasses). Maintained by another Django core developer.
Django doesn't offer such model polymorphism out of the box.The easiest way to do what you are trying to achieve is to store the content type of a new object in it. There's a simple generic app called django-polymorphic-models which offers you this functionality - and - additionally a downcast
-method that will return the child object!
From the docs:
If you have a
Place
that is also aRestaurant
, you can get from thePlace
object to theRestaurant
object by using the lower-case version of the model name...
It's not particularly pretty or efficient, but the best way I can think of implementing this without storing the subclass meta data in the DB (like django-polymorphic-models does) would be a child()
method in your ThingsThatMigrate
model class:
from django.core.exceptions import ObjectDoesNotExist
def child(self):
for subclass in self.__class__.__subclasses__():
try:
return getattr(self, subclass.__name__.lower())
except (AttributeError, ObjectDoesNotExist):
continue