Search code examples
djangodjango-modelsdjango-ormdjango-inheritance

Django model inheritance on other model's field


I have the following models:

class Engine(models.Model):
    ...
    def speed(self):
        return 100
    objects = InheritanceManager()

class TurboEngine(Engine):
    ...
    def speed(self):
        return 500

class Car(models.Model):
    ...
    engine = models.ForeignKey(Engine)

No I am using a DetailView on a Car with a TurboEngine, but car.engine.speed() returns 100. How do I make it select the right class?


Solution

  • This is a very nice and interested question, we had the same problem 2 or 3 years ago:

    class RealInstaceProvider(object):
        def get_real_instance(self):
            """
            Makes a SQL sentence which does the JOIN with its real model class
            """
    
            if hasattr(self, '_real_instance'):  # try looking in our cache
                return self._real_instance
            subclasses = self.__class__.__subclasses__()
            if not subclasses:  # already real_instance
                real_instance = getattr(self, self.__class__.__name__, self)
                self._real_instance = real_instance
                return real_instance
            else:
                subclasses_names = [cls.__name__.lower() for cls in subclasses]
                for subcls_name in subclasses_names:
                    if hasattr(self, subcls_name):
                        self._real_instance = getattr(self, subcls_name, self).get_real_instance()
                        return self._real_instance
                self._real_instance = self
                return self
    
    class Engine(models.Model, RealInstaceProvider):
        ...
        def speed(self):
            return 100
        objects = InheritanceManager()
    
    class TurboEngine(Engine):
        ...
        def speed(self):
            return 500
    
    class Car(models.Model):
        ...
        engine = models.ForeignKey(Engine)
    

    And you must to access of the next way: car.engine.get_real_provider().speed()