Model Inheritance Patterns
Use abstract base classes, multi table inheritance, and proxy models. Pick the right approach for reuse, performance, and admin behavior.
1. Introduction
Django supports three types of model inheritance: abstract base classes, multi-table inheritance, and proxy models. Each solves a different problem and has different performance and admin implications.
Choosing the wrong type leads to unnecessary database joins, confusing admin behavior, or duplicated code. This guide explains each type clearly so you can pick the right one for your situation.
- You should already be comfortable defining models, fields, and relationships.
- Your
.venvmust be active and Django 5.2 installed.
2. Abstract base classes
An abstract model defines shared fields and methods that other models inherit. Django does not create a database table for the abstract model itself — only for the child models that inherit from it.
Use abstract base classes when you want to share common fields across multiple models without any database relationship between them.
from django.db import models
class TimeStampedModel(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True # no table is created for this model
class Article(TimeStampedModel):
title = models.CharField(max_length=200)
content = models.TextField()
class Comment(TimeStampedModel):
text = models.TextField()
article = models.ForeignKey(Article, on_delete=models.CASCADE)
Both Article and Comment get their own tables with created and updated columns. There is no join required — the fields are copied directly into each child table.
TimeStampedModel like the one above is a standard pattern you will see in almost every serious Django project.
3. Multi-table inheritance
Multi-table inheritance creates a separate database table for both the parent and each child model. Django links them with an automatic OneToOneField. Every child instance has a corresponding parent row.
class Content(models.Model):
title = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Article(Content):
body = models.TextField()
slug = models.SlugField(unique=True)
class Video(Content):
url = models.URLField()
duration = models.PositiveIntegerField(help_text='Duration in seconds')
Django creates three tables: pages_content, pages_article, and pages_video. Fetching an Article requires a JOIN between pages_content and pages_article.
# Accessing parent fields works transparently
article = Article.objects.get(id=1)
article.title # from Content table
article.body # from Article table
# Access child from parent
content = Content.objects.get(id=1)
content.article # raises RelatedObjectDoesNotExist if not an Article
ForeignKey relationships over multi-table inheritance.
4. Proxy models
A proxy model shares the same database table as its parent but can have different Python behavior — different methods, properties, ordering, or admin configuration. No new table is created.
Use proxy models when you want a different view or behavior on the same data without touching the original model.
class Article(models.Model):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, default='draft')
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class PublishedArticleManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class PublishedArticle(Article):
objects = PublishedArticleManager()
class Meta:
proxy = True
ordering = ['-created']
verbose_name = 'Published Article'
def publish(self):
self.status = 'published'
self.save()
Both Article and PublishedArticle read from and write to the same pages_article table. But PublishedArticle.objects.all() only returns published articles, and it has its own admin entry.
Article.objects.all() # all articles
PublishedArticle.objects.all() # only published articles
5. Which one to use
- Abstract base class — use when you want to share fields and methods across unrelated models with no database relationship between them. This is the right choice in most situations.
- Multi-table inheritance — use when you genuinely need to query the parent model and retrieve a mix of child types together. Rare in practice. Be aware of the JOIN cost.
- Proxy model — use when you want different behavior, ordering, or admin representation for the same data. No database changes, no joins, no performance cost.
7. Next steps
You now understand all three inheritance patterns and when to apply each. The next step covers migrations — how Django tracks model changes and applies them to the database safely.