Skip to content

Django Model Inheritance

Polymorphism is the provision of a single interface to entities of different types. You need a versatile data model that allows you to store diverse content that is accessible through a single interface.

Using model inheritance

Django supports model inheritance. It works in a similar way to standard class inheritance in Python. Django offers the following three options to use model inheritance:

  1. Abstract models:

    Useful when you want to put some common information into several models.

  2. Multi-table model inheritance:

    Applicable when each model in the hierarchy is considered a complete model by itself.

  3. Proxy models:

    Useful when you need to change the behavior of a model, for example, by including additional methods, changing the default manager, or using different meta options.

Let’s take a closer look at each of them.

Example

An abstract model is a base class in which you define the fields you want to include in all child models. Django doesn’t create any database tables for abstract models. A database table is created for each child model, including the fields inherited from the abstract class and the ones defined in the child model.

To mark a model as abstract, you need to include abstract=True in its Meta class. Django will recognize that it is an abstract model and will not create a database table for it. To create child models, you just need to subclass the abstract model.

Python
from django.db import models

class BaseContent(models.Model):
    title = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True

class Text(BaseContent):
    body = models.TextField()

In this case, Django would create a table for the Text model only, including the title, created, and body fields.

In multi-table inheritance, each model corresponds to a database table. Django creates a OneToOneField field for the relationship between the child model and its parent model.

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. The following example shows multi-table inheritance:

Python
from django.db import models

class BaseContent(models.Model):
    title = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)

class Text(BaseContent):
    body = models.TextField()

Django will include an automatically generated OneToOneField field in the Text model and create a database table for each model.

A proxy model changes the behavior of a model. Both models operate on the database table of the original model. To create a proxy model, add proxy=True to the Meta class of the model. The following example illustrates how to create a proxy model:

Python
from django.db import models
from django.utils import timezone

class BaseContent(models.Model):
    title = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)

class OrderedContent(BaseContent):
    class Meta:
        proxy = True
        ordering = ['created']

    def created_delta(self):
        return timezone.now() - self.created

Here, you define an OrderedContent model that is a proxy model for the Content model. This model provides a default ordering for QuerySets and an additional created_delta() method. Both models, Content and OrderedContent, operate on the same database table, and objects are accessible via the ORM through either model.

Creating the Content models

The Content model of your courses application contains a generic relation to associate different types of content with it. You will create a different model for each type of content. All Content models will have some fields in common and additional fields to store custom data. You are going to create an abstract model that provides the common fields for all Content models.

Edit the models.py file of the courses application and add the following code to it:

Note

models.py
from django.db import models
from django.contrib.auth.models import User

class ItemBase(models.Model):
    owner = models.ForeignKey(User, related_name='%(class)s_related', on_delete=models.CASCADE)
    title = models.CharField(max_length=250)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

        def __str__(self):
            return self.title

class Text(ItemBase):
    content = models.TextField()

class File(ItemBase):
    file = models.FileField(upload_to='files')

class Image(ItemBase):
    file = models.FileField(upload_to='images')

class Video(ItemBase):
    url = models.URLField()

Reference