Skip to content

Django Model Fields with TextChoices

Before Django 3.0, defining choices for model fields typically involved using tuples of tuples, which could be cumbersome and error-prone. With the introduction of TextChoices, Django provides a more elegant and Pythonic way to define choices using class-based syntax.

from django.db import models
from django.utils.translation import gettext_lazy as _

class MyModel(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'draft', _('Draft')
        PUBLISHED = 'published', _('Published')
        ARCHIVED = 'archived', _('Archived')

    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.DRAFT
    )

In this example:

  • We define a Status class as a subclass of TextChoices.
  • Each choice is defined as a class attribute, with the first value representing the database representation and the second value providing a human-readable name.
  • The status field of the MyModel model uses Status.choices for its choices.

Benefits of TextChoices:

  1. Improved Readability: Using TextChoices enhances the readability of your code by providing a clear and concise way to define choices within your model definitions.
  2. Enhanced Maintenance: With TextChoices, managing and updating the list of choices becomes more straightforward, reducing the chances of errors and inconsistencies.
  3. Autocomplete Support: Modern code editors often provide autocomplete support for class-based definitions, making it easier to work with TextChoices compared to tuples of tuples.
  4. Integration with Localization: TextChoices seamlessly integrates with Django's internationalization (i18n) and localization (l10n) features, allowing for easy translation of choice labels.

Conclusion:

In conclusion, TextChoices is a valuable addition to Django's feature set, offering a more elegant and Pythonic way to define choices for model fields. By leveraging class-based syntax, TextChoices improves the readability, maintainability, and usability of Django models, making them easier to understand and maintain.

Whether you're working on a small project or a large-scale application, consider incorporating TextChoices into your Django models to streamline your development workflow and enhance the quality of your code.


Life Before TextChoices

1. Tuples of Tuples (Old Style):

  • This is the traditional way of defining choices in Django.
  • Choices are defined as tuples of tuples, where each inner tuple contains the value stored in the database and its human-readable representation.
STATUS_CHOICES = (
    ('draft', 'Draft'),
    ('published', 'Published'),
    ('archived', 'Archived'),
)

Use Case:

This approach is suitable for simple cases where the list of choices is small and does not change frequently.

2. NamedTuples:

  • NamedTuples provide a more structured approach compared to tuples of tuples.
  • Choices are defined using NamedTuples, which allows for better readability and maintainability.
from collections import namedtuple

Status = namedtuple('Status', ['value', 'display_name'])
STATUS_CHOICES = [
    Status('draft', 'Draft'),
    Status('published', 'Published'),
    Status('archived', 'Archived'),
]

Use Case:

NamedTuples are useful when you want a more organized and maintainable way to define choices compared to tuples of tuples.

3. Class-Based Choices (TextChoices):

  • Introduced in Django 3.0, TextChoices provides a modern and Pythonic way to define choices using class-based syntax.
  • Choices are defined as class attributes within a subclass of TextChoices.
from django.db import models
from django.utils.translation import gettext_lazy as _

class Status(models.TextChoices):
    DRAFT = 'draft', _('Draft')
    PUBLISHED = 'published', _('Published')
    ARCHIVED = 'archived', _('Archived')

Use Case:

TextChoices is recommended for new projects or when you want to refactor existing code to improve readability and maintainability. It offers benefits such as autocomplete support and integration with localization.

4. Dynamic Choices:

  • Sometimes, you may need to dynamically generate choices based on certain conditions.
  • Choices can be generated dynamically using a function or by querying a database.
def get_status_choices():
    # Logic to generate choices dynamically
    return [('draft', 'Draft'), ('published', 'Published')]

class MyModel(models.Model):
    status = models.CharField(max_length=20, choices=get_status_choices)

Use Case:

Dynamic choices are suitable when the list of choices depends on runtime conditions or needs to be fetched from external sources like a database.


When choosing the appropriate method for defining choices, consider factors such as readability, maintainability, flexibility, and the specific requirements of your project. For new projects, TextChoices is recommended for its modern syntax and additional features, but the other methods still have their place depending on the complexity and context of your application.