Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django multi-table inheritance, how to know which is the child class of a model?

I'm having a problem with multi-table inheritance in django.

Let’s make an example with bank accounts.

class account(models.Model):
    name = models……

class accounttypeA(account):
    balance = models.float…..

    def addToBalance(self, value):
        self.balance += value

class accounttypeB(account):
    balance = models.int…. # NOTE this

    def addToBalance(self, value):
        value = do_some_thing_with_value(value) # NOTE this
        self.balance += value

Now, i want to add a value to an accounttype, but all i have is an account object, for instance acc=account.object.get(pk=29) . So, who is the child of acc ?

Django automatically creates an account_ptr_id field in accounttypeA and accounttypeB. So, my solution was:

child_class_list = ['accounttypeA', 'accounttypeB']

for cl in child_class_list:
    try:
        exec(“child = ” + str(cl) + “.objects.select_for_update().get(account_ptr_id=” +              str(acc.id) + “)”)
        logger.debug(“Child found and ready to use.”)
        return child
    except ObjectDoesNotExist:
        logger.debug(“Object does not exist, moving on…”)

Maybe it's a drawing board problem at this point! :)

I hope I have been clear in my example. Thanks

like image 494
luistm Avatar asked Sep 13 '12 16:09

luistm


People also ask

What is your understanding of Django model inheritance?

Models inheritance works the same way as normal Python class inheritance works, the only difference is, whether we want the parent models to have their own table in the database or not. When the parent model tables are not created as tables it just acts as a container for common fields and methods.

What is multi-table inheritance Django?

In multi-table inheritance, each model corresponds to a database table. Django creates a OneToOneField field for the relationship in the child's model to its parent. To use multi-table inheritance, you have to subclass an existing model. Django will create a database table for both the original model and the sub-model.

What is class Meta in Django models?

Model Meta is basically the inner class of your model class. Model Meta is basically used to change the behavior of your model fields like changing order options,verbose_name, and a lot of other options. It's completely optional to add a Meta class to your model.


2 Answers

To the best of my knowledge there isn't a Django built-in way to do this.

However, given acc=account.object.get(pk=29), you can use:

try:
    typeA = acc.accounttypeA
    # acc is typeA
except accounttypeA.DoesNotExist:
    # acc should be typeB if account only has typeA and typeB subclasses

try:
    typeB = acc.accounttypeB
    # acc is typeB
except accounttypeB.DoesNotExist:
    # acc should be typeA if account only has typeA and typeB subclasses
like image 57
K Z Avatar answered Sep 22 '22 00:09

K Z


my solution was based on this

class account(models.Model):
    name = models……

    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__) and type(attr) != type(self):                 
                    return attr
            except:
                pass

    @staticmethod
    def allPossibleAccountTypes():
        #this returns a list of all the subclasses of account (i.e. accounttypeA, accounttypeB etc)
        return [str(subClass).split('.')[-1][:-2] for subClass in account.__subclasses__()]

    def accountType(self):
        try:
            if type(self.cast()) == NoneType:
                #it is a child
                return self.__class__.__name__
            else:
                #it is a parent, i.e. an account
                return str(type(self.cast())).split('.')[-1][:-2]
        except:
            logger.exception()
    accountType.short_description = "Account type"

class accounttypeA(account):
    balance = models.float…..

    def addToBalance(self, value):
        self.balance += value

class accounttypeB(account):
    balance = models.int…. # NOTE this
like image 20
nmz787 Avatar answered Sep 21 '22 00:09

nmz787