The Ins and Outs of Model Inheritance by Blythe J Dunham

This video features Blythe J Dunham at DjangoCon US 2019 in San Diego, California, USA.

The Ins and Outs of Model Inheritance by Blythe J Dunham
0:23:38
Published October 25, 2019
2,312 views

DjangoCon 2019 - The Ins and Outs of Model Inheritance by Blythe J Dunham

This talk was presented at: https://2019.djangocon.us/talks/the-ins-and-outs-of-model-inheritance/

LINKS:
Follow Blythe J Dunham 👇
On GitHub: https://github.com/blythedunham
Official homepage: https://www.linkedin.com/in/blythedunham/

Follow DjangCon US 👇
https://twitter.com/djangocon

Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/

Intro music: "This Is How We Quirk It" by Avocado Junkie.
Video production by Confreaks TV.
Captions by White Coat Captioning.

Summary

Django supports abstract, multi-table, and proxy model inheritance, but each maps to relational tables differently and has important trade-offs. Abstract models reuse fields without creating a parent table; multi-table inheritance preserves normalized parent and child tables but can cause extra joins and queries; proxy models share one table while changing behavior, managers, or filtering. Single-table inheritance and generic foreign keys offer alternatives, while JSON fields trade queryability and data integrity for flexibility. The central recommendation is to prefer composition—such as one-to-one profiles or explicit relationships—when it expresses the domain more clearly and makes database behavior and performance easier to understand.

Key takeaways

  • Abstract models are useful for sharing substantial fields and behavior, but the abstract parent cannot be queried or referenced directly.
  • Multi-table inheritance makes parent attributes easy to query, but accessing subclass data can create one query per subclass and hurt performance as the hierarchy grows.
  • Proxy models keep a single database table and change Python behavior, managers, or filtering without adding fields.
  • Single-table inheritance can be fast because it uses one query, but it produces a wide, potentially cluttered table and is not built into Django.
  • Generic foreign keys and JSON fields provide flexible alternatives, but weaken referential integrity, validation, and sometimes query performance.
  • Composition through foreign keys or one-to-one relationships is often a more explicit and natural database design than inheritance.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction and Overview Blythe Dunham introduces the talk and outlines Django model inheritance, composition, and alternative designs.
  2. 1:00 Composition Versus Inheritance The talk contrasts has-a composition with is-a inheritance and explains their differing relationships to relational databases.
  3. 3:22 Abstract Model Inheritance Abstract models provide reusable fields and behavior without creating a parent database table.
  4. 6:33 Multi-Table Inheritance Multi-table inheritance creates linked tables for concrete parent and child models, with benefits and query-performance tradeoffs.
  5. 11:15 Proxy Models Proxy models reuse a single table while changing model behavior, managers, filtering, or ordering.
  6. 14:25 Downcasting and Single-Table Inheritance The talk covers subclass downcasting and single-table inheritance, including their performance benefits and schema costs.
  7. 16:43 Generic Foreign Keys Generic foreign keys provide a flexible polymorphic relationship but weaken type safety, referential integrity, and query optimization.
  8. 19:53 Unstructured JSON Data JSON fields offer a schema-flexible alternative to inheritance while making querying, validation, and data integrity more difficult.
  9. 21:25 Composition-Based Design The speaker encourages rethinking inheritance hierarchies and modeling many relationships through profiles and other composed objects.
  10. 22:16 Explicit Relationships and Conclusion The talk concludes by recommending explicit fields or one-to-one relationships when they make behavior and performance clearer.

Transcript

3,426 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:15

Welcome friends, welcome. Sorry about the technical difficulties. I needed my speaker notes. So I'm Blythe Dunham and I'm just venturing out on my own to freelance as Snow Giraffe. I've been doing Django for about three years at rover. com, the largest network of trusted dog sitters and walkers. And now we support cats and hire humans. So prior to that, I worked on Ruby on Rails for almost a decade. And since I've been in the tech industry for over 20 years, you could probably say that I've spent a lot of time hanging out with models. Thank you. The jokes get better. So as you can tell from the name snow giraffe, I really love snow sports and giraffes.

1:00

And so I thought I'd include them in today's adventure. We're going to be talking about composition and inheritance, the three types of model inheritance that Django supports, two alternatives to model inheritance. And then avoiding inheritance altogether. So who's seen this before? Okay, I think I put it in the abstract, so So in 1994, a book called Design Patterns came out, where they advised folks doing object-oriented design to prefer composition over inheritance because it's more flexible. So what does this mean? Composition is a mechanism to combine objects or data into more complex ones. You can think of it as a has

1:45

a relationship. For example, a giraffe has a blue tongue. Inheritance is a way of deriving a subclass from a parent or base class to create a hierarchy of shared attributes and methods. You can think of inheritance as the is a relationship. So a giraffe is a glorious animal. So while inheritance provides a way to avoid repeating yourself, It's more obvious and natural for us to build association between objects than it is to try to find commonalities and organize them into a hierarchy. So the plot thickens. When we think about how objects map to the database via the ORM, the object relational mapping, we find that composition is really intuitive and has a natural mapping.

2:34

However, inheritance isn't even supported by relational databases. Therefore, we have several different approaches to choose from. Since each has its own ins and outs, it's important to choose wisely, or better yet, rethink the problem using composition. So let's look at composition in Django. We have a giraffe class. It has a name field We have a tongue class and it has a one-to-one field back to giraffe. Now if giraffes had multiple tongues, I would use well that would be scary and I would use a foreign key. I'd use a foreign key type here instead. for the one-to-many relationship. So if we look at the object-oriented UML diagram, we represent our objects with a one-to-one

3:22

relationship. And this looks super similar to the entity relational diagram or ERD that represents our database schema. The objects are represented as tables and the foreign keys are used to show the association. So unfortunately, model inheritance is a little bit more awkward. It doesn't have that natural mapping. So up first is abstract models. The point of abstract models is to reuse the parent classes, fields, and field-related functionality. So, as the name suggests, the parent is abstract and not backed by a table in the database. Therefore, each derived class will have all of the fields from the parent and itself on its own table.

4:09

So in Django, we have an animal, parent class, that subclasses model. And we have a name field, we have a method for speak that returns gibberish. And we have overridden the metaclass definition to set abstract to true. Giraffe subclasses animal. We add a field for the number of spots that the giraffe has, and we override speak to return hum, because that's what giraffes do. You just can't hear it. It's infrasonic. So the ERD diagram looks like this. Assuming I've named my Django app abstract, we have an abstract giraffe table. We have an integer auto-incremented field for the ID. We have the name field from the parent animal

4:56

and the spots count from the the giraffe class. If we were to override our sorry subclass. Animal again was zebra, it would have that idea name field, and then anything that it adds like a stripe count So when we query for our giraffes, this is gonna go query against the abstract giraffe table. We can't query with animal. objects. all because that animal table doesn't exist. And so when we call speak on the giraffe, it returns hum because we've overridden that method. So use cases. Abstract models work best when there's a lot of duplicated fields. If there's only a few fields, it's better to be explicit and just define them on each model.

5:45

So great examples include any sort of base or core model functionality. That all or many of your models inherit. For example, in two scoops of Django, it walks you through the timestamp model, which is also implemented in Django extensions. And what it does is it adds an added and modified date time fields that are updated when the record is saved. So you could use that with any of your models, if it's a giraffe, it's a location, if it's a customized user, anything like that. So the advantages of abstract models are that you can easily reuse the parent class's fields and field-related logic. However, the parent class can't be used in isolation.

6:33

So, if you have any related models, you can't have an animal ID. You'll need to have a zebra ID and a giraffe ID. Okay, this is my favorite slide in the hall deck. And the photographer Atif Saeh granted me permission to use it so I could warn you about using multiple table inheritance. Don't get eaten by the lion. So multitable inheritance is defined like this. In Django, we have a big cat parent class with a name field, and we have a subclass, lion, that adds giraffes hunted, and a method called speak. Now notice that I haven't overridden

7:20

the metaclass definition. This is vanilla out-of-the-box model inheritance in Django. So, first, this is called concrete inheritance because the parent class is concrete. We have a big cat table in the database with that ID and a name field. The Lion table has a pointer, a big cat pointer, which is a foreign key back to Big Cat, and then it adds any fields of its own like giraffes hunted. Now, notice we don't have an ID field here, which is not usually the default in Django. So the primary key of the lion table is this big cat pointer ID. If we subclass Big cat with cheetah and cheetah has none of its own fields, then we still have that big cat

8:07

pointer ID. So notice here that you could implement this explicitly with one-to-one relationships. If you want it. What happens when we query? Let's try to get all of the lions. This executes a query on the lion table joined to the big cat table. And what this does is it allows you to access the big cat instance, the big cat pointer, without executing an additional query. You can also call any of the fields or methods on the parent directly on lion. So you can say lion. name. The problem starts when we try to get all

8:53

of the species of cats, regardless of lion, cheetah, whatever. So it starts out simple, give me all the big cats, we'll run a query on the big cat table. Then I want to access the speak method on the child. But I don't know if this is a cheetah or a lion, because that foreign key is on the cheetah and lion fields. So I run a query on the cheetah table, and it's not a cheetah. So we get an exception. And then we can try again with Lion. So this time it works, it returns roar, but we've executed another query on the Lion table. So what this means is for each record you have, you'll execute up to n queries

9:40

where n is the number of subclasses. So if you add another subclass, then your performance might be degraded. But wait, you say, I love to earload and optimize everything. Okay, that's great. Good for you. And second of all, you're still going to have to do a prefetch query or a select related, which causes an evil left join per subclass. So a good use case for multiple table inheritance is the classic shopping cart. We're a travel store, we sell trips with start and end dates, and we sell t-shirts with sizing information. So the car or the order has a many-to-many relationship with product, which means I have a join table here.

10:27

And If I just need that name and pricing information, I don't have to follow the pointer to the trip and clothing classes, then it's not really a performance problem. If in addition, if I only have a few products in a cart at a time, then you can follow that foreign key and it shouldn't be too terrible. So, in conclusion, the advantages of multiple table inheritance are that all the common parent attributes can be queried easily together. However, when you start accessing those subclasses, it could lead to inefficient queries that hurt performance and make scaling difficult. A lot of this is lack of understanding of what's happening under the covers. So sometimes it is better to be more explicit

11:15

because if your coworker adds a subclass two years down the road, you might find yourself having performance problems. Okay, last but not least, we have proxy models. The purpose of proxy models is to override the behavior and functionality of the parent class. So we have exactly one table to rule them all. So, for lack of a better word, everyone in Middle-earth is a person. This is our parent class. They have a name and a person type that I'll talk about in a minute. And there's a method called characteristic that returns Middle Earth Dweller. Hobbit subclasses person, it sets proxy to true on the metaclass definition. And it defines a method characteristic that returns hairy feet.

12:03

So again, there's one precious table, no matter how many subclasses you add. And when we access this via Hobbit, we'll get back a Hobbit instance and calling characteristic will return hairy feet. If we access it through person, we'll use the same row in the database, the same record, but when we call characteristic, we'll get back middle earth dweller. So we basically just change the behavior. Of the subclass. So what cool thing you can do with this is uh add a custom manager. So this elf has an elf manager. The ELF manager overrides create to set the person type to E when the record is inserted.

12:51

We also override the query set to filter on person type equals E. So this means if we query person. objects. all, then we'll get back instances of person for Frodo and Legolas. However, if we query with ELF, we get back only Legolas as an ELF instance. Because Frodo is a hobbit. So again, one table, we're just changing the where clause or the sorting order of the columns that we select. It's against proxy person, which is our one and only table. The advantages of proxy models are that it's really easy to modify the subclass 's behavior. The disadvantages are that fields used by any subclass must be defined

13:39

for everyone on that one table. Use cases are things like an ordered model where you change the sorting to sort on like an added field, or an active model where you filter out deactivated models if you're doing soft deletes. And you can create a custom user model, but it might be better to think of that as a one-to-one relationship between user and user profile. So one thing you can do with proxy models is downcasting and single-table inheritance. Downcasting is a way to cast instances into the p the subclass. So normally when we query with person, it returns a person

14:25

in sorry, a person in instance, when you call characteristic, it returns Middle Earth Dweller. With downcasting, it will return a hobbit and an elf instance. And the way that this works is we have a type field and we set it with the class name. So we do one query, we get that class name, and then we instantiate the correct subclass. And so when we get Frodo and call characteristic, we've never wanted hairy feet so much. So, downcasting is not supported out of the box. You can use Django typed models, or you could do it pretty cheaply and quickly on your own. Uh there's an article called Django

15:11

STI on the cheap. There is also downcasting packages for multiple table inheritance, but you have to be careful because you will still incur that extra query or select related depending on the implementation. So the advantages of single table inheritance is performance, performance, performance. One table means one query. The disadvantage is that since each subclass, all of the fields have to be represented on that one table, it can lead to clutter and bloat. So some people call this denormalization of all the data on one table for performance. The use cases for

15:56

single-table and multiple table inheritance are really similar. The shopping cart scenario would work. The way that you can choose between them is ask yourself, do most of the subclasses share fields and functionality? And if so, and you want a performance boost, single table inheritance might be appropriate If they're vastly different, then multiple table inheritance is preferable. So now that we've gone through all this, I'm going to tell you that sometimes the best type of model inheritance is not to use inheritance at all. And so we have a couple of alternative approaches, and then we have some ways to rethink the problem. So this guy, it's not really good to play with generic explosives all the time.

16:43

Um We have something called generic foreign keys in Django to implement polymorphism. Polymorphism is the ability of an object to take on many forms, as you saw with inheritance. So your homework is to look up how to define this in Django, but in short, a generic foreign key fakes a real foreign key with two fields. The first field is the content type ID, and that is a foreign key to the Django Content Types table that holds the name and the app label for all of your concrete models across all of your apps. The other field is an object ID, which is just an integer. So we put the ID of the related model here. you could put

17:29

zero or nonsense data. It's just an integer field. And so for that reason, we have a very weak relationship to anything you want it to be. It could be a blog, it could be a giraffe, it could be a location, it doesn't matter. So the advantages are that you can use any model, you don't have to do another migration. The use cases for generic foreign keys are things like tags and comments, where the related object can be anything, like a blog post or a giraffe or a location, and the object, the comment is not usually accessed outside of the blog post or giraffe or related object So, what I mean is, your typical use case would be like: I have a blog post, give me all of my comments for this particular blog post

18:19

If you're asking the question like, give me all the comments in the world, I don't care what the object related to it is, and then I need to go look into this object and do another query to see what it is, then you might consider using single table or multiple table inheritance. So the disadvantages are that code can become hard to maintain. If you're using dynamic type checking, Then two years down the road, you might not remember what this object is that you're passing around in all your methods. Another disadvantage is in order to access those objects from the scenario where we have all the comments in the world You need you can't use select related, so you're going to have to write custom SQL if you want to optimize the performance.

19:04

The other major disadvantage is that there's no referential integrity. I think of referential integrity as A seatbelt. So you can drive your car down the road 100 miles an hour, but you probably want to put on a seatbelt So this means that you have nothing to prevent you from putting dirty data in the database. If you delete a record, it's just an object, the object ID field is just an integer, so it might not cascade through. And you Could just end up with a little bit of unclean data. So the second alternative to model inheritance is unstructured data. We have JSON field for Postgres and there's a JSON fields package that you can use with other databases.

19:53

Now what we're doing here is we're taking a bunch of fields and we're serializing them and we're just shoving them into the database So this can avoid the clutter that you see with single-table inheritance because each subclass just uses that one database field to jam in whatever it wants. And similarly, it avoids the need for related objects as with multiple table inheritance. The disadvantages are that it's pretty tough to query against unstructured fields. Postgres will let you do it, but for the most part, you want to just put data into the database and not index against it or query against it. The other disadvantage is that you lose data integrity.

20:39

It's not enforced by the database again. And so All the validation has to be on the application level. And then this can lead to dirty data since we're just putting in exactly what each subclass wants. And you make a change, so you have another blob, and some of your data might be a little bit dirty. Okay, so we've gotten through the two alternatives. But maybe we could rethink this a little bit. This lady um is going into Corbett's Color in Jackson Hole, and she probably could have rethought her approach a little bit. This is the easy entrance. Um she made it and I went in after her, so it was it's all good. But

21:25

my point is just because objects share attributes, it doesn't mean we should represent them together in a hierarchy. Like a human and a beetle both have legs, but they're not inherently similar, or most of them aren't. The good news is that most is a relationships can be expressed as a has a relationship. So a user is a seller, or a user has a seller profile. This is that one-to-one relationship I was talking about. With user you can create a profile instead of subclassing it. Another example is a manager is an employee or an employee has a managerial job. And when you rethink the problem with composition, you can take advantage of that natural mapping.

22:16

And the last thing I wanted to say was sometimes it's good to be explicit. Sometimes you can use multiple foreign keys instead of inheritance like proxy models. Or maybe if you only have one field to repeat, you can just add it to multiple models. And finally, with multiple table inheritance. You won't get the bells and whistles that Django provides, but it's a lot more explicit to implement it as a one-to-one with one-to-one fields. So everybody in your organization knows what's going on, and you will recognize the fact that when you add a subclass, your performance will be degraded. So your future self will thank you. And I thank you too. I really appreciate and a big shout out to all the organizers and volunteers at to make DjangoCon so special.

23:05

Please feel free to hit me up on all of the normal means. I've put the slides and the uh the code that I used at Blyth Dunham DMI for Django Model Inheritance on GitHub. And thank you very much.

Questions this talk answers

What are abstract models in Django, and when should I use them?

An abstract model provides reusable fields and field-related methods without creating its own database table; each concrete subclass gets those fields on its own table. They work best for functionality shared by many models, such as timestamp fields, but the parent cannot be queried or used independently.

Discussed at 3:22

How does multi-table inheritance work in Django, and what are its performance problems?

Django gives the concrete parent and each subclass separate tables, linking them with a parent pointer that is also the subclass’s primary key. Querying a subclass joins the tables, while discovering subclass types from parent objects can require up to one query per subclass and make scaling difficult.

Discussed at 6:37

What are Django proxy models used for?

Proxy models reuse the parent’s single database table while changing Python behavior, such as methods, managers, filtering, or ordering. They are useful for things like ordered models, soft-delete filtering, or different views of the same records, but all subclass fields must exist on the shared table.

Discussed at 11:15

What is single-table inheritance in Django, and when is it a good choice?

Single-table inheritance stores every subclass in one table and uses a type field to instantiate the appropriate subclass, so reads can be handled with one query. It offers strong performance when subclasses share fields and functionality, but can create a wide, cluttered table with many unused fields.

Discussed at 14:25

What is a generic foreign key in Django, and when should I use one?

A generic foreign key uses a content-type ID and an object ID to point to almost any model, making it useful for tags or comments attached to different kinds of objects. It sacrifices referential integrity and makes broad queries and optimization harder, so it is less suitable when related objects must be queried together.

Discussed at 16:43

What are the drawbacks of storing model-specific data in a JSON field?

Putting varying fields into JSON or another unstructured field avoids the extra columns of single-table inheritance and the related objects of multi-table inheritance. However, querying and indexing are difficult, database validation is lost, and application-level changes can leave inconsistent or dirty data.

Discussed at 19:53

Should I use composition instead of model inheritance in Django?

Often, yes: many apparent “is-a” relationships can be represented as “has-a” relationships with one-to-one or foreign-key associations, such as a user having a seller profile. Composition maps more naturally to relational databases and avoids many of inheritance’s query and schema complications.

Discussed at 21:25

When should I implement inheritance explicitly with one-to-one relationships?

Using explicit one-to-one fields can make the relationship and its performance costs clearer to everyone maintaining the system, even though Django’s built-in inheritance conveniences are lost. It is especially useful when you want to avoid surprising behavior from adding subclasses later.

Discussed at 22:16

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from DjangoCon US