One of the principles of Python is “Do not repeat yourself”. Abstract models in Django are meant to do exactly that.
If you are designing a database for a school, there would be database models representing all types of people who attend this school which includes the students, teachers, cleaning staff, cafeteria staff, school bus drivers, etc.
However, all of these folks have some common information such as name, date of birth, date of joining, address and contact information, etc.
It doesn't seem right to repeat all of these fields for each database model while writing the code, and this is where abstract models work great.
How to create an abstract base model?
All you have to do is create a base model with all the common fields and set abstract = True in the meta class.
This will ensure that there is no actual database table created for this base model and it is meant only for being inherited by other models that actually become tables in the database.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=255)
date_of_birth = models.DateTimeField()
date_of_joining = models.DateTimeField()
address = models.TextField()
class Meta:
abstract = True
class Student(Person):
roll_number = models.IntegerField()
class Teacher(Person):
compensation = models.CharField(max_length=255)
After running migrations, there will be a student table and a teacher table created in your database while the person model stays in your codebase ready to be inherited in another model if you chose to.
Abstract as much as possible
The “Do not repeat yourself” principle does not have to end here. For instance, even though the folks belonging to the cleaning staff and cafeteria staff are also persons, they both have shift timings which students and teachers do not.
from django.db import models
#======= Abstract models =========#
class Person(models.Model):
name = models.CharField(max_length=255)
date_of_birth = models.DateTimeField()
date_of_joining = models.DateTimeField()
address = models.TextField()
class Meta:
abstract = True
class SchoolEmployee(Person):
compensation = models.CharField(max_length=255)
class Meta:
abstract = True
class HiredHelp(SchoolEmployee):
shift_timings = models.JSONField()
class Meta:
abstract = True
#--------------------------------#
#======= Actual models ==========#
class Student(Person):
roll_number = models.IntegerField()
class Teacher(SchoolEmployee):
qualifications = models.JSONField()
class CafeteriaEmployee(HiredHelp):
pass
class CleaningStaffMember(HiredHelp):
pass
#--------------------------------#
Depending on your requirement you can have further abstract models that inherit from other abstract models.
Closing notes
It is important to keep in mind that abstract models by themselves are quite useless. There is no database table created for them and hence they are of no consequence.
Abstract models are only useful if you intend to create other models which inherit from the abstract ones.
Abstract classes are a good example of how the concept of inheritance can be used to write reusable code.
Top comments (9)
In my opinion, this article describes a really easy way how to painfully shoot yourself in the foot when you store your data in SQL databases.
In my experience, introducing abstract models in Django leads to unsolvable N+1 query issues in the long run.
Imagine following scenario:
Imagine you have a model called AccessCard.
This AccessCard has properties value (int) and owner (Person). When you create a table listing 100 AccessCards per page, showing both the AccessCard.value and AccessCard.owner.full_name, is there a way, how to provide these data this using just 1 SQL query instead of 1+100?
If I remember correctly, it is easy when abstract classes are not used. See QuerySet.prefetch_related / QuerySet.select_related. However when I was trying to find a solution for this with inheritance in play, I failed miserably.
In my experience with ORM mappers in Django and with Doctrine from the PHP world, using inheritance in conjunction with SQL is a bad idea.
The time you save by not typing again the definitions are definitely lost when you try to optimize the horribly inefficient queries.
I do not want to degrade the value of your article, I just want to point out the downsides and that this approach has, in my opinion, significant drawbacks which should be pointed out.
You can use
QuerySet.prefetch_related
orQuerySet.select_related
with Abstract Base Classes in Django.Let's say we have these models:
If you make this query you wouldn't have a N+1 issue:
You have one query only. The generated SQL is:
Maybe you were referring to something else?
I find this to be a very bad example of using abstract base classes. Certainly it shows the repeated use of common fields from a base entity. Just because you can doesn't mean you should.
Per Django docs, there are different ways to inherent, this article only expresses one method.
The very heart of the article suggests DRY, yet the implementation fails that. A "teacher" could also be a "student" and a "student" could work part time as "cleaning staff" or "cafeteria staff", and so on.
For each case, the "teacher" would have a name, and other Person fields, and likewise the "student" would have a name and other Person fields - hence violating the very DRY reasoning for the article's depiction of inheritance.
A more accurate depiction of real world modeling would be to model a person and then add by relationship the various roles they may play.
Within Django, it would seem that given the concept of an app being a single full concept, a Person app would be a basis for modeling a real person. Then a different app for each of the other person roles would capture the additional fields and behavior associated with that particular role, all related, as an extension of a person.
Regards,
.. Otto
As per what I’ve observed, abstract models aren’t written in the DB. So there’s no Table for an abstract model.
The Django ORM, maps the fields to each model it’s subclassing only at the time of migrations.
However, when in the case of Inheritance, Django ORM fails miserably. N+1 queries, INNER JOINS are way too common which ultimately make the DB Queries VERY slow.
My recommendation is to use Abstract Tables only when needed, and avoid Inheritance completely when developing using Django. Instead use OneToOne/ForeignKey relationships to extend the tables.
Thanks 🍻
I don't follow what you're saying.
If an abstract model isn't creating tables you don't have a N+1 issue.
I've commented here:
dev.to/guzmanojero/comment/2ac2g
Can you expand?
Very well explained! Simple & easy to understand. I have also written article on how abstract base classes work in Python. Read here: dev.to/dollardhingra/understanding...
This is a great article. Thanks for sharing!
Ohh this is something real nice thanks for sharing back to DRY from the darkness :)
It is easy to forget DRY when one is deep into development. So hoping this post serves as a reminder