migrating django-model field-name change without losing data

Viewed 58329

I have a django project with a database table that already contains data. I'd like to change the field name without losing any of the data in that column. My original plan was to simply change the model field name in a way that would not actually alter the name of the db table (using the db_column column parameter):

The original model:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

The new model:

class Foo(models.Model):
  name = models.CharField(max_length=50, db_column='orig_name')

But, running South's schemamigration --auto produces a migration script that deletes the original column, orig_name, and adds a new column, name, which would have the unwanted side effect of deleting the data in that column. (I'm also confused as to why South wants to change the name of the column in the db, since my understanding of db_column was that it enables a change to the model field name without changing the name of the database table column).

If I can't get away with changing the model field without changing the db field, I guess I could do a more straight forward name change like so:

The original model:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

The new model:

class Foo(models.Model):
  name = models.CharField(max_length=50)

Regardless of which strategy I end up using (I would prefer the first, but would find the second acceptable), my primary concern is ensuring that I don't lose the data that is already in that column.

Does this require a multi-step process? (such as 1. adding a column, 2. migrating the data from the old column to the new column, and 3. removing the original column) Or can I alter the migration script with something like db.alter_column?

What is the best way to preserve the data in that column while changing the column's name?

11 Answers

Django 2.0.9 (and onwards) can automatically detect if a field was renamed and gives an option to rename instead of delete and create a new one

(same works for Django 2.2) enter image description here

Initial answer

Posting, if it's still helpful for someone.

For Django 2.0 + simply rename the field in the model

class Foo(models.Model):
    orig_name = models.CharField(max_length=50)

to

class Foo(models.Model):
    name = models.CharField(max_length=50)

Now run python manage.py makemigrations It'll generate migration with operations for removing the old field and adding the new one.

Go ahead and change that to following.

operations = [
    migrations.RenameField(
        model_name='foo',
        old_name='orig_name',
        new_name='name')
]

Now run python manage.py migrate it'll rename the column in DB without losing data.

UPDATE In Django 3.1 it is quite simple for changing only one field at a time.

In my case:

The old field name was: is_admin The new field name was: is_superuser

When I make migrations by python manage.py makemigrations it asked me do I want to rename the field or not. And I just hit y to rename. Then I migrate by python manage.py migrate. The terminal history in my case looks like: enter image description here

NOTE: I did not test with more than one field at a time.

As pointed out in the other responses, it is now quite easy to rename a field with no changes on the database using db_column. But the generated migration will actually create some SQL statements. You can verify that by calling ./manage.py sqlmigrate ... on your migration.

To avoid any impact on your database you need to use SeparateDatabaseAndState to indicate to Django that it doesn't need to do something in DB.

I wrote a small article about that if you want to know more about it.

This is for Django 4.0. Let's do this with an example.

My original field name was anticipated_end_date, I need to name it tentative_end_date. Follow the following steps to complete the operation

  1. Change the anticipated_end_date to tentative_end_date inside the model
  2. run python manage.py makemigrations. Ideally, it would show the following message

Was your_model_name.anticipated_end_date renamed to your_model_name.tentative_end_date (a DateField)? [y/N]

If it shows this message, then just press y and you are good to migrate, as it will generate the correct migration. However, if makemigrations command does not ask about renaming the model field, then go inside the generated migration and change the operations content the following way:

    operations = [
        migrations.RenameField(
            model_name='your_model_name',
            old_name='anticipated_end_date',
            new_name='tentative_end_date',
        ),
    ]

Now you can run python manage.py migrate.

This way your model field/DB column will be renamed, and your data will not be lost.

1.Edit the field name on the django model

2.Create an empty migration like below:

$ python manage.py makemigrations --empty testApp (testApp is your application name)

  1. Edit the empty migration file which is created recently

    operations = [ migrations.RenameField('your model', 'old field’, 'new field'), ]

  2. Apply the migration
    $ python manage.py migrate

Database column name will be altered with new name.

Related