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
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.
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.
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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With