Django Abstract Base Class - Model Inheritance

Model inheritance is a very useful and powerful feature of django, but used incorrectly it can cause a lot of confusion. Lets go ahead and take a look at some of those.

Have you ever been looking through other peoples code and seen a model class Meta with abstract = True? That is the first type of inheritance we are going to look at, abstract base models. There is also multi-table inheritance, proxy models, and multiple inheritance available.

Abstract Base Classes

In this post we are going to focus on Abstract base classes. This is the easiest type of model inheritance to really understand because it works very much like normal inheritance. In OOP when you inherit from another object you get all of its members plus your own. You can modify those members or leave them the same. Abstract base classes are the same.

Here are two example classes

class Customer(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(max_length=100)
    address = models.CharField(max_length=100)
    purchase_history = models.ForeignKey('cart.Invoice')

class Staff(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(max_length=100)
    address = models.CharField(max_length=100)
    bio = models.TextField()
    position = models.CharField(max_length=20)

As you can see these two models are legitimate models with fields, but they do have common elements. In a lot of cases it is fine to leave them this way. However, if we use these same fields yet again it would be a good idea to use an abstract base class.

class ConactInfo(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(max_length=100)
    address = models.CharField(max_length=100)

    class Meta:
        abstract = True

class Customer(ContactInfo):
    purchase_history = models.ForeignKey('cart.Invoice')

class Staff(ContactInfo):
    bio = models.TextField()
    position = models.CharField(max_length=20)

We now have a ConactInfo abstract base class because we added abstract = True to its Meta. This will tell django and the migration system this isn't a model we can use to store data with.

However, we can inherit from it so that each model that we subclass with it has the fields, methods, and properties of the abstract model. At this point if you did ./manage.py makemigrations it would create a migration file creating only Customer and Staff tables in the database.

Will it Work with Existing Models?

Lets say you have several existing models in your code base you want to abstract into base classes. Will it "just work"? Yes it will. Once I found out about abstract classes I went and made several then ran a makemigrations and nothing happened because to django nothing changed you just refactored you code.

Conclusion

Hopefully this gives you a place to start with doing model inheritance. It is very useful, but much like OOP don't go crazy with it and have a lot of abstract classes. You can be so abstract you have no idea what is going on.

My rule of thumb, whether good or bad, is if I have more than 3 fields repeated in only 2 models I just leave them alone. If I need to add them to a third model I will evaluate if it makes sense to use an abstract model. If I can name the abstract model and it makes sense like ContactInfo then there is enough context to create the new model, so I will. Otherwise I will put it off until the code is screaming for an abstract base class.

Stay tuned for the next post on Multi-table inheritance.

What has your experience been?

comments powered by Disqus