Handling Django choices and migrations
If you are using Django's choices
option on a model field, you have probably come across the situation where adding / updating a choice generates a new migration. If you are using choices liberally for things that are variable over time (e.g. "where did you hear about us?") this can generate a lot of unnecessary migrations.
Take this example:
class Person(models.Model):
class Source(models.TextChoices):
ADVERT = ("advert", "Saw an advert")
REFERRAL = ("referral", "Referred by a friend")
SEARCH = ("google", "Found on Google")
source = models.CharField(
max_length=10,
choices=Source.choices,
help_text="How did you hear about us?"
)
If you run makemigrations
against this it will generate a migration that looks something like:
class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name="Person",
fields=[
("id", models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True)
),
("source", models.CharField(
max_length=10,
help_text="How did you hear about us?",
# this tuple matches the state of the underlying SOURCE_FOO
# and if that changes a new migration will be generated by
# makemigrations
choices=(
("advert", "Saw an advert"),
("referral", "Referred by a friend"),
("google", "Found on Google"),
))
)
]
)
]
The choices are baked into the migration in their raw text form. If you change this (even so much as adjusting a spelling mistake), and rerun makemigrations
you will get a new migration.
The solution is to hack the migration code to use the dynamic values defined in
the Source
choices class:
operations = [
migrations.CreateModel(
name="Person",
fields=[
("id", models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True)
),
("source", models.CharField(
max_length=10,
help_text="How did you hear about us?",
# this will always match the current state
# of Source.choices, and so will not generate a
# new migration if the underlying values change
choices=Person.Source.choices
)
]
)
]
If you now try amending the choices, rerunning makemigrations
will not generate a new migration, but the choices will be updated.