Why should I set max_length when using Choices in a Django model?
Asked Answered
A

4

7

In the official Django 2 tutorial I found this:

from django.db import models

class Student(models.Model):
    FRESHMAN = 'FR'
    SOPHOMORE = 'SO'
    JUNIOR = 'JR'
    SENIOR = 'SR'
    YEAR_IN_SCHOOL_CHOICES = (
        (FRESHMAN, 'Freshman'),
        (SOPHOMORE, 'Sophomore'),
        (JUNIOR, 'Junior'),
        (SENIOR, 'Senior'),
    )
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

Now my question is does using choices mean that only either of the four defined values is valid for this field? If so what is the use of specifying the max_length? If not, why not use a validator that validates if the value is actually exacly one of the specified ones or at least a validator that only accepts an specific length not just an upper bound.

Adriatic answered 25/5, 2018 at 13:53 Comment(2)
I don't understand what you mean by "only either of the four defined values is valid". Any of those values is valid. You need max_length because the database needs to know how many characters to allocate to the field.Grappa
I mean like would this model trow an exception if a value like 'A' or 'RU' is set to it that is not defined in the choices?Adriatic
D
10

The max_length is enforced at the database level, but the choices are enforced at python code level (when calling full_clean() or clean_<fieldname>()).

They are independent of each other.

If you set a value to your field other than the specified choices and you don't call instance.full_clean() or instance.clean_<fieldname>(), it could still get saved to the database without raising errors.

But if you use djangos forms, the choices validation is done for you (the form calls full_clean()) and you don't need to worry about it.


This means, for example, if you set max_length smaller than your largest option in choices, your database will silently truncate the values for that field or raise a DatabaseError; either way you will not get it to work.

This separation is useful, for example, if you want to add more choices later; if the new options are not larger than max_length, there will be no need to to change the database structure (that means, the new migration will NOT issue alter table SQL statements).

Dustproof answered 25/5, 2018 at 14:9 Comment(2)
Oh thanks. so now do you think it's better that I use a regex validator to make sure that it will be exactly one of the options?Adriatic
No, the validators are run at the same level (python code) than the choices validation. As long as you use forms or call instance.full_clean(), the choices validation will be done and there is no need for other validators.Dustproof
O
1

I ran into this issue recently, except that I wasn't just using a two letter code so it was a bit tedious to make sure I had a valid max_length. I ended up doing something like this:

year_in_school = models.CharField(
    max_length=max(len(v[0]) for v in YEAR_IN_SCHOOL_CHOICES),
    choices=YEAR_IN_SCHOOL_CHOICES,
    default=FRESHMAN,
)

If I ever add an option that exceeds the existing max length, makemigrations will detect that and add that change to the migration.

Oxblood answered 26/4, 2022 at 16:34 Comment(0)
F
0

I found it convenient to extend TextChoices with a property that can be used to set max_length:

TextChoicesMeta = type(models.TextChoices)


class ExtendedTextChoicesMeta(TextChoicesMeta):
    @property
    def max_length(cls) -> int:
        return max(len(value) for value in cls.values)


class ExtendedTextChoices(models.TextChoices, metaclass=ExtendedTextChoicesMeta):
    pass


class PropertyType(ExtendedTextChoices):
    RESIDENTIAL = "RESIDENTIAL", _("residential")
    COMMERCIAL = "COMMERCIAL", _("commercial")
    INDUSTRIAL = "INDUSTRIAL", _("industrial")
    RAW_LAND = "RAW_LAND", _("raw land")
    SPECIAL_USE = "SPECIAL_USE", _("special use")


class Property(models.Model):
    property_type = models.CharField(
        choices=PropertyType.choices,
        max_length=PropertyType.max_length,
    )
Finial answered 30/7, 2022 at 13:35 Comment(0)
H
0

For django 3 and highter and models.TextChoices

class DocumentTypeChoices(models.TextChoices):
    PHOTO = 'PHOTO', _('Photo')
    DOC = 'DOC', _('Document')

...

document_type = models.CharField(
    choices=DocumentTypeChoices.choices,
    max_length=max(len(choice) for choice, _ in DocumentTypeChoices.choices),
)
Hexagram answered 19/7, 2023 at 9:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.