Skip to content

Instantly share code, notes, and snippets.

@blackrobot
Created October 2, 2024 17:38
Show Gist options
  • Save blackrobot/29eacfff4c449578236d68b4a1ce074f to your computer and use it in GitHub Desktop.
Save blackrobot/29eacfff4c449578236d68b4a1ce074f to your computer and use it in GitHub Desktop.
Two Scoops Django 3.x Best Practices: 6.1.2 Be Careful With Model Inheritance

6.1.2 Be Careful With Model Inheritance

Model inheritance in Django is a tricky subject. Django provides three ways to do model inheritance: abstract base classes, multi-table inheritance, and proxy models.

Warning

Django Abstract Base Classes ≠ Python Abstract Base Classes
Don’t confuse Django abstract base classes with the abstract base classes in the Python standard library’s abc module, as they have very different purposes and behaviors.

Here are the pros and cons of the three model inheritance styles. To give a complete comparison, we also include the option of using no model inheritance to begin with:

Model Inheritance Style Pros Cons
No model inheritance: If models have a common field, give both models that field. Makes it easiest to understand at a glance how Django models map to database tables. If there are a lot of fields duplicated across models, this can be hard to maintain.
Abstract base classes: Tables are only created for derived models. Having the common fields in an abstract parent class saves us from typing them more than once. We don’t get the overhead of extra tables and joins that are incurred from multi-table inheritance. We cannot use the parent class in isolation.
Multi-table inheritance: Tables are created for both parent and child. An implied OneToOneField links parent and child. Gives each model its own table, so that we can query either parent or child model. It also gives us the ability to get to a child object from a parent object: parent.child. Adds substantial overhead since each query on a child table requires joins with all parent tables. We strongly recommend against using multi-table inheritance. See the warning below.
Proxy models: A table is only created for the original model. Allows us to have an alias of a model with different Python behavior. We cannot change the model’s fields.

Caution

Avoid Multi-Table Inheritance
Multi-table inheritance, sometimes called “concrete inheritance,” is considered by the authors and many other developers to be a bad thing. We strongly recommend against using it. We’ll go into more detail about this shortly.

Here are some simple rules of thumb for knowing which type of inheritance to use and when:

  • If the overlap between models is minimal (e.g., you only have a couple of models that share one or two identically named fields), there might not be a need for model inheritance. Just add the fields to both models.
  • If there is enough overlap between models that maintenance of models’ repeated fields causes confusion and inadvertent mistakes, then in most cases the code should be refactored so that the common fields are in an abstract base model.
  • Proxy models are an occasionally useful convenience feature, but they’re very different from the other two model inheritance styles.
  • At all costs, everyone should avoid multi-table inheritance (see the warning above) since it adds both confusion and substantial overhead. Instead of multi-table inheritance, use explicit OneToOneFields and ForeignKeys between models so you can control when joins are traversed. In our combined 20+ years of doing Django, we’ve never seen multi-table inheritance cause anything but trouble.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment