Table of contents

Understand inheritance types in Django models

Understand inheritance types in Django models

Sometimes, when we create Models in Django we want to give certain characteristics in common to several of our models. Probably, the first approach that would come to our mind would be to repeat the fields over and over again. This would bring us two problems; first, we are repeating information; second, if we want to add another field in common we will have to modify each of the models. This problem is solved by Django’s model inheritance.

# Nota como se repiten múltiples campos en los dos modelos
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=150)
    description = models.TextField()
    manufacter = models.ForeignKey(Manufacter, on_delete=models.CASCADE)
    modified = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

class Manufacturer(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()
    modified = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

# ... otros diez modelos con los mismos campos abajo

Inheritance types in Django

There are three types of inheritance available and each behaves differently at the table level:

  • Abstract
  • Multi table
  • Proxy

For this example I will be using Django version 3.1 and Python 3.7.

Abstract Inheritance

This type of inheritance allows us to put a variety of fields in common that we want the models that inherit from it to include. To define a model as Abstract just add the Meta class containing an attribute called abstract equal to True. Django will not create any table for a model with Meta.abstract = True.

from django.db import models

class BasicData(models.Model):
    modified = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True

class Product(BasicData):
    name = models.CharField(max_length=150)
    description = models.TextField()

class ShippingMethod(BasicData):
    name = models.CharField(max_length=150)
    description = models.TextField()
    price = models.PositiveIntegerField()

In the example above both models will include the modified and created fields, however Django will not create any tables for the BasicData model.

Multi Table Inheritance

In this type of inheritance Django will create a table for each model** (that’s why it’s called multi-table). It will also join both models automatically by means of an OneToOneField field in the child model.

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=150)
    address = models.CharField(max_length=150)
    modified = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)

class Cafe(Place):
    number_of_employees = models.IntegerField()
    speciality_coffee_available = models.BooleanField(default=False)

In the example above we may be interested in having both models, we can filter by Place and then we can access the child by its one to one relationship **using its lower case model name.

myFavoriteCafe = Place.objects.get(name="Matraz cafe")
print("Matraz Cafe has {} employees".format(myFavoriteCafe.cafe.number_of_employees))

Proxy inheritance

This type of inheritance is used to change or extend the behavior of a model. To create it just add the Meta class with the proxy attribute equal to True. In this case both models are in the same table and we can create, access, update or delete data using any of its models.

from django.db import models

class BaseProduct(models.Model):
    modified = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)
    name = models.CharField(max_length=150)

    def __str__(self):
        return "{} created at {}".format(self.name, self.created.strftime("%H:%M")) 

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

In the example above we have a new model that defines a default ordering by means of the ordering attribute. That is, assuming we had a table with data we could access the same data from the Django ORM.

from app.models import BaseProduct, OrderedContent

# Mismos datos, orden predeterminado
BaseProduct.objects.all()
<QuerySet [<BaseProduct: Eterno resplandor de una mente sin recuerdos created at 21:59>, <BaseProduct: Arrival created at 22:00>, <BaseProduct: The imitation game created at 22:01>]>

# Mismos datos, orden inverso
OrderedContent.objects.all()
<QuerySet [<OrderedContent: The imitation game created at 22:01>, <OrderedContent: Arrival created at 22:00>, <OrderedContent: Eterno resplandor de una mente sin recuerdos created at 21:59>]>

As you can see we were able to access the same three database objects from both models, with the difference that in the OrderedContent model our objects appear sorted in descending order with respect to the created field.

If you want to know more about Django, I can recommend some books. Read my review of two scoops of django, a great book that teaches you good Django Framework practices.

Eduardo Zepeda
Web developer and GNU/Linux enthusiast always learning something new. I believe in choosing the right tool for the job and that simplicity is the ultimate sophistication. I'm under the impression that being perfect is the enemy of getting things done. I also believe in the goodnesses of cryptocurrencies outside of monetary speculation.
Read more