开发者

Find object in child class from object in parent class in django

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 in fact a Coconut or a Swallow? Once having done so, how can I get to the Coconut or Swallow object?


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!


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.


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


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.


From the docs:

If you have a Place that is also a Restaurant, you can get from the Place object to the Restaurant object by using the lower-case version of the model name...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜