Polymorphic Model Inheritance in Django

Ok, so I think I'm going to answer my own questions...

  1. Is this a good case for 'multi-table' inheritance?

    It appears so. Although there are a few places that recommend against 'multi-table' inheritance (listed here for example), some counterpoints are:

    • @Bruno Desthuilliers points out that these opinions are not from the 'official' documentation, and by extension, he implies that 'multi-table' is a perfectly good feature available for one to use.

    • My reading of @dhke's links and comments is that you have to choose one option, and that the 'multi-table' option is the only way databases truly support inheritance. I.e. Even with the polymorphic tricks of tools like Hibernate or SQLAlchemy, you are still choosing whether to JOIN tables ('multi-table' option) for object lookup or to UNION tables ('abstract base' / 'polymorphic' options) for set creation.

    • @dhke also points out that it is probably better to use the 'multi-table' option and tell a library like django-polymorphic not to do subclass resolution when looking up the 'whole set' rather than have the database do a UNION over all of the tables (as would be required for 'whole set' lookup with the 'abstract base class' option).

  2. Am I trying too hard to force the type inheritance? Should I just get rid of Tool? If yes, then do I have to create a *ToolGroup model for each subclass?

    No, it doesn't seem that way. The two uses of the Tool interface that I presented have different needs:

    • The ToolGroup / hierarchical-grouping use case is a good one for retaining the inherited Tool class. This would get very ugly if you had to create a type-specific set of classes for every type of tool

    • The ToolAttribute also makes a good case for the super class, except if you are able to use things like the HSTORE field type (provided by Postgres, I'm not sure about other backends). This link gives a good rundown, and it is probably what I will do here (Thanks to @nigel222 for the research that went into the question!).

  3. What is the current (Django 1.8) accepted way around this? Surely I am not the first person to build a relational system in Django ;-) And the fact that other ORMs have considered this problem suggest it is a common design choice.

    This is now an irrelevant question. Basically they don't worry about it.

  4. Is the polymorphic solution (possibly via SQLAlchemy) an option? Is this being considered for Django 1.9+?

    Not that I can tell.


The case that led me to this question is a model like this:

class PurchasableItem(models.Model):

    class Meta:
        abstract = True


class Cheesecake(PurchasableItem):
    pass


class Coffee(PurchasableItem):
    pass

The workaround I used is turning the parent class into an attribute:

class PurchasableItem(models.Model):

    class Meta:
        abstract = False


class Cheesecake(models.Model):
    purchasable_item = models.OneToOneField(PurchasableItem, on_delete=models.CASCADE)


class Coffee(models.Model):
    purchasable_item = models.OneToOneField(PurchasableItem, on_delete=models.CASCADE)

This way, I can get both the behavior and querying functionality.