Skip to content

Model Inheritance

Django provides three distinct styles of model inheritance. The style you choose depends on whether you want the parent models to have their own database tables or just hold common information.

Abstract Base Classes

Use this style when you want to put common information into several models effectively. You do this by writing a base class and adding abstract = True to the Meta class.

Django does not create a database table for abstract base classes. Instead, when you create a child model, Django takes the fields from the abstract base and adds them directly to the child model's table.

A common use of an abstract base model is to include fields like created_at and updated_at, which child models can then inherit. This eliminates repeated code and follows the DRY (Don't Repeat Yourself) principle that Django encourages.

python
# Abstract Base Class
class TimeTrackedModel(Model):
    created_at = DateTimeField(auto_now_add=True)
    updated_at = DateTimeField(auto_now=True)

    class Meta:
        # This makes the model abstract, meaning no table will be created.
        abstract = True
        
# Child model that inherits the timestamp fields
class Person(TimeTrackedModel):
    first_name = CharField(max_length=30)
    last_name = CharField(max_length=30)

Multi-table Inheritance

Use this style when you need each model in the hierarchy to have its own database table. This happens automatically if you subclass an existing model (and do not mark it as abstract).

Django creates a table for the parent model and a separate table for the child model. The child model is linked to the parent via an automatically-created OneToOneField.

python
class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

Under the hood, the automatically-created OneToOneField looks like this:

python
place_ptr = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    parent_link=True,
    primary_key=True,
)

You generally do not need to define this yourself. However, you can override this field if you want to control the name of the link or specific behavior. To do so, declare your own OneToOneField on the child model and set parent_link=True.

Child models inherit all fields from their parent, even though the data lives in separate database tables. This means you can use fields from the parent model directly when filtering the child model.

python
# Filter the parent directly
Place.objects.filter(name="Bob's Cafe")

# Filter the child using the parent's field
Restaurant.objects.filter(name="Bob's Cafe")

If you have a Place object that is also a Restaurant, you can access the child object from the parent by using the lowercase name of the child model:

python
p = Place.objects.get(id=12)

# If p is also a Restaurant, this returns the Restaurant object.
print(p.restaurant)

In the above example, if p is not a restaurant, it will result in Restaurant.DoesNotExist exception.

Proxy Models

Use this style when you want to change the Python behavior of a model without changing its database structure.

A proxy model operates on the exact same database table as its parent. It is useful for adding new methods, changing the default manager, or setting new default ordering options on an existing model. You define this by adding proxy = True to the Meta class.

python
class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        pass

Multiple Inheritance

Django models can inherit from multiple parent models, just like standard Python classes. Python's name resolution rules apply here. For example, if multiple parents define a Meta class, Django uses only the first one it finds and ignores the others.

Generally, you should avoid complex inheritance hierarchies. The best use case for multiple inheritance is for "mix-ins", which are classes designed to add a specific extra field or method to any model that inherits them.

If you inherit from multiple models that both use the default auto-generated id primary key, Django will raise an error. You can fix this in two ways:

  • Explicit AutoField: Manually define a primary key on each parent model.
python
class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    # ...

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    # ...

class BookReview(Book, Article):
    pass
  • Common Ancestor: Create a shared base model for the ID, and use an explicit OneToOneField to link the parents to it.
python
class Piece(models.Model):
    pass

class Article(Piece):
    article_piece = models.OneToOneField(
        Piece, on_delete=models.CASCADE, parent_link=True
    )

class Book(Piece):
    book_piece = models.OneToOneField(
        Piece, on_delete=models.CASCADE, parent_link=True
    )

class BookReview(Book, Article):
    pass

Field Name "Hiding" (Overriding Fields)

In standard Python, a child class can override any attribute of its parent. In Django, this behavior is restricted for model fields.

If a parent model (that is not abstract) has a field named author, you cannot create a field or attribute named author in the child model. Doing so raises a FieldError.

This restriction exists because overriding fields complicates database table creation and instance initialization. However, it only applies to model fields; you can still override normal Python attributes.

You can, however, override fields inherited from an abstract model. You can replace the field with a new one, or remove it entirely by setting field_name = None.