How to update removed model choices in Django without breaking existing database values?

I'm working on a Django project where we use models.TextChoices for fields like current_status. Our client requested wording updates — for example, renaming "Contacted" to "Contacted for assessment", "Booked" to "Assessment booked", and so on.

Previous Enum:

class ClientStatus(models.TextChoices):
    CONTACTED = "Contacted"
    BOOKED = "Booked"

Updated Enum:

class ClientStatus(models.TextChoices):
    CONTACTED_FOR_ASSESSMENT = "Contacted for assessment", "Contacted for assessment"
    ASSESSMENT_BOOKED = "Assessment booked", "Assessment booked"

Problem

We already have hundreds of users in the database with:

current_status = 'Contacted'

Since "Contacted" no longer exists in the model choices:

  1. Will migrations fail or behave unexpectedly?
  2. Will the Django Admin or API views break when encountering these legacy values?
  3. What is the correct way to rename choice values without leaving inconsistent data?

Constraints

We want to avoid creating unnecessary migrations. We want this to work for all environments (dev/staging/production). We'd prefer not to break the Admin panel or serializers.


Question

What is the safest and most Django-idiomatic way to rename or remove enum values when old data already exists in the database?

Is it okay to do manual SQL updates? Or is there a recommended Django migration approach?

What I've Tried

Ran python manage.py makemigrations and migrate. Migration did not fail. Admin view throws errors when trying to edit users with old values. I considered a manual SQL fix:

UPDATE api_user
SET current_status = 'Contacted for assessment'
WHERE current_status = 'Contacted';

you should just edit the models and then execute the sql code to make sure that there is no way any record would get "contacted" as the current_status , you are on the right track .

From your description it seems like you only need presentational / display changes. If that's the case and storing the previous values ("Contacted", "Booked") in the database is fine just continue with that and only change the display value:

class ClientStatus(models.TextChoices):
    CONTACTED = "Contacted", "Contacted for assessment"
    BOOKED = "Booked", "Assessment booked"

Note that in the code above the first value in the tuple is the same as what you had before and the only change is adding a display value explicitly. Django forms, etc. will automatically use the display value, if you need to render that in templates manually use the get_FOO_display method (Note the foo should be replaced with field name)

If you actually want to change the values in the database as well then update the enum like you did and update the data in the database. The recommended method to update the data is to create a data migration. Run python manage.py makemigrations --empty yourappname (replace "yourappname") and then modify the generated migration:

from django.db import migrations


def update_choices(apps, schema_editor):
    # We can't import the Person model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    YourModel = apps.get_model("yourappname", "YourModel")
    YourModel.objects.filter(your_field_name="Contacted").update(your_field_name="Contacted for assessment")
    YourModel.objects.filter(your_field_name="Booked").update(your_field_name="Assessment booked")

class Migration(migrations.Migration):
    # Other stuff like dependencies

    operations = [
        migrations.RunPython(update_choices),
    ]
Вернуться на верх