Django: Choosing Between TextChoices and IntegerChoices in Django Models

Yash Marathe
Django Unleashed
Published in
6 min readSep 3, 2023

Table of contents

  1. Introduction
  2. Setting up a sample Django project
  3. Experiment Analysis
  4. Observations
  5. Conclusion

Introduction

When it comes to developing robust and efficient Django applications, the choices you make in defining your models can significantly impact both the usability and performance of your project. One such choice revolves around how you represent choices or enumerations in your models.

Django offers two primary methods for handling choices within models: TextChoices and IntegerChoices. These powerful tools allow you to define a predefined set of options for a field, such as a status field in an order tracking system or a category field for blog posts.

I’ve worked under multiple seasoned Senior Engineers who hold strong opinions about which of these methods to prefer.

Some prefer TextChoices because they make the code more readable and easier for other developers, especially those not familiar with the codebase, and database analysts to understand what each choice represents even if they don’t have access to the codebase.

Others prefer IntegerChoices because they are more space-efficient and can make database queries slightly faster compared to text-based choices, as integer comparisons are usually faster.

Both choices have their own sets of pros and cons.

But is one choice better than the other when it comes to scalability?

I decided to experiment with both these choices and compare the difference in space and the speed of database queries w.r.t. different numbers of records.

Setting up a sample Django project

  1. Create a Django Project: If you haven’t already, create a new Django project by running:
django-admin startproject choices_experimentation

2. Create a Django App: Inside your project directory, create a Django app:

python manage.py startapp app

3. For this experiment, I have used PostgreSQL.

4. Define the Models: In your app’s models.py, define two models, one using models.TextChoices and the other using models.IntegerChoices.

5. We will need some utility functions for the experiment. These are defined below.

5.1. A function to populate the above models.

5.2. A function to get the size of the models.

5.3. A function to display the query results.

Now, the setup for the experiment is done.

Experiment Analysis

1. Let’s try with 5000 records.

add_random_data(count=5000)

Upon calling the size function for the particular models, these were the results —

For the model with TextChoices , the size is 40 kB more than the model IntegerChoices.

There isn’t a considerable difference in the sizes of both the models.

Now, let’s check the time taken for a filter query.

Similar to the storage, there isn’t a considerable difference in the filter query.

NOTE: The filter query is used only for counting the records here with the respective filter. There is no indexing involved. The query performs a full table scan.

2. Let’s increase the number of records to 100k records. We will add 95k more records.

add_random_data(count=95000)

Upon calling the size function for the particular models, these were the results —

Alright, now the difference has grown from 40kB to 1 MB!!

Now, let’s check the time taken for a filter query.

However, the filter time is still almost the same. Even if the number of records has increased, the filtering operation for IntegerChoice status and TextChoice status is still being performed at a similar speed.

3. Let’s increase the number of records to 1 million.

add_random_data(count=900000)

Upon calling the size function for the particular models, these were the results —

For 1 million records, the size of the model with text choices has increased by 8 MB now.

It’s clear that the storage required is growing considerably as we are increasing the number of records in the table.

Now, let’s check the time taken for a filter query to see if there is any difference in the query time like the storage space.

Nope, the query is still able to perform a full table scan with an almost similar speed irrespective of integer or text choices.

4. What if we add 10 million more records? Let’s check.

add_random_data(count=10000000)

For a total of 11 million records, the size of the model with text choices has increased by 87 MB now.

Now, for the last time, let’s check the filter query time.

Still no difference in the filter query time.

Observations

  1. There is a considerable increase in the storage space in the model with TextChoices.

2. The filtering query time remains almost the same irrespective of the filter data type.

I was a little surprised by this result since it seems a little counterintuitive. Apparently, a lot of other people have experienced the same. Here is a link in which they have discussed this in detail —

https://stackoverflow.com/questions/2346920/sql-select-speed-int-vs-varchar#:~:text=bigint%20vs%2016%2Dchar%20text%20makes%20no%20difference%20in%20speed.

If any of you talented folks can know a more concrete reason behind this, do contact me!

Conclusion

In conclusion, the choice between modTextChoices and models.IntegerChoices in Django, models depend on the trade-offs that align with your project's priorities.

If clarity, readability, and self-documentation are paramount, models.TextChoices provides a structured and human-friendly way to define choices, making your code more comprehensible and maintainable. However, it may result in slightly larger database storage.

On the other hand, models.IntegerChoices offers space-efficient storage if you are going to have millions of records in your table, making it suitable for scenarios where resource optimization is crucial. However, it sacrifices some readability and can lead to confusion without proper documentation.

Ultimately, the decision should consider your project’s specific needs and the balance between code clarity and database efficiency. In some cases, a hybrid approach, combining both text and integer choices for the best of both worlds, might be the most pragmatic solution.

👏 Your 3 claps mean the world to me! If you found value in this article, a simple tap on those clapping hands would make my day.

🚀 Please consider following for more tech-related content.

🌟 If you found this blog helpful and would like to stay updated with more content, feel free to connect with me on LinkedIn.

--

--

Yash Marathe
Django Unleashed

🤖 Backend dev turning code experiments into tech symphonies. I write about the trials, triumphs, and real-world magic in backend, databases, and ML. 🚀📝