[Fixed]-How do I find the "concrete class" of a django model baseclass

16đź‘Ť

âś…

Django implements model inheritance with a OneToOneField between the parent model’s table and the child model’s table. When you do Base.object.all(), Django is querying just the Base table, and so has no way of knowing what the child table is. Therefore, unfortunately, it’s not possible to go directly to the child model instance without additional queries.

This snippet shows a common method of adding a ContentType field to the base model:

from django.contrib.contenttypes.models import ContentType

class Base(models.Model):
    content_type = models.ForeignKey(ContentType,editable=False,null=True)

    def save(self):
        if(not self.content_type):
            self.content_type = ContentType.objects.get_for_model(self.__class__)
        self.save_base()

    def as_leaf_class(self):
        content_type = self.content_type
        model = content_type.model_class()
        if(model == Base):
            return self
        return model.objects.get(id=self.id)

You can then say if Base.content_type.model_class() to determine the type.

Here is another snippet that adds a custom manager into the mix.

As you can see, both of these solutions have the potential to be extremely expensive. If you have a large number of instances, using the as_leaf_class() method will require one query on each item.

Instead, if you have a known set of child models, simply query each model separately and aggregate the instances into one list.

👤Daniel Naab

6đź‘Ť

Have a look at InheritanceManager in django-model-utils – attaching it to a model gives you the concrete child classes (at least at the first level):

from model_utils.managers import InheritanceManager

class Base(models.Model):
    objects = InheritanceManager()

# ...

Base.objects.all().select_subclasses() # returns instances of child classes

model-utils requires Django 1.2 or higher.

1đź‘Ť

Slightly modified version of what Daniel Naab proposed:

from django.contrib.contenttypes.models import ContentType
from django.db import models

def ParentClass(models.Model):
    superclass = models.CharField(max_length = 255, blank = True)

    def save(self, *args, **kwargs):
        if not self.superclass:
            self.superclass = ContentType.objects.get_for_model(self.__class__)

        super(ParentClass, self).save(*args, **kwargs)

    def getChild(self):
        s = getattr(self, self.superclass)
        if hasattr(s, 'pk'):
            return s
        else:
            return None

class Child1(ParentClass):
    pass

class Child2(ParentClass):
    pass
👤alexpirine

0đź‘Ť

Well… My problem was. In a view, I had this principal model, lets say “Big_Model” and there were some “Small_Model” related to “Big_Model”. So when I wanted to retrieve all “Small_Model” related to a certain instance of “Big_Model” I did that **_set.all() stuff. But the point is that Small_Model has Child Classes and I wanted, in views.py, to get which child class was each of the Small_Model instances related to. My trick was to define boolean methods in model Small_Model like is_child_1() and is_child_2(). And when it is true, you apply the actual child pointer instead of the Small_Model pointer.

Ok… Thats not clear enough, still I dont have much time to write a good example, so i’ll just copy-paste my case here:

class Cache(models.Model):
  valor = models.DecimalField(max_digits=9, decimal_places=2, blank= True, null= True)
  evento=models.ForeignKey(Evento)
  def __unicode__(self):
    return u'%s: %s' % (self.evento, self.valor)
  class Meta:
    verbose_name='CachĂŞ'
    verbose_name_plural='CachĂŞs'
  def is_cb(self):
    try:
      self.cache_bilheteria
      return True
    except self.DoesNotExist:
      return False
  def is_co(self):
    try:
      self.cache_outro
      return True
    except self.DoesNotExist:
      return False
👤Dhiana Deva

-2đź‘Ť

It feels brittle because it is. (This is a reprint of an answer in a different context. See C++ casting programmatically : can it be done ?)

Read up on polymorphism. Almost every “dynamic cast” situation is an example of polymorphism struggling to be implemented.

Whatever decision you’re making in the dynamic cast has already been made. Just delegate the real work to the subclasses.

You left out the most important part of your example. The useful, polymorphic work.

When you said “I want to determine if the object is of type Child_1 or Child_2…” you left out the “so I can make the object do aMethod() in a way that’s unique to each subclass”. That method is the useful work, and it should simply be a method of both subclasses.

class Base(models.model):
    def aMethod(self):
        # base class implementation.

class Child_1(Base):
    def aMethod(self):
        # Child_1 override of base class behavior.

class Child_2(Base):
    def aMethod(self):
        supert( Child_2, self ).aMethod() # Invoke the base class version
        # Child_2 extension to base class behavior.

Same method, multiple implementations. Never a need to “run-time type identification” or determining the concrete class.

👤S.Lott

Leave a comment