migrating django-model field-name change without losing data

回眸只為那壹抹淺笑 提交于 2019-11-29 23:54:58

Changing the field name while keeping the DB field

Adding an answer for Django 1.8+ (with Django-native migrations, rather than South).

Make a migration that first adds a db_column property, and then renames the field. Django understands that the first is a no-op (because it changes the db_column to stay the same), and that the second is a no-op (because it makes no schema changes). I actually examined the log to see that there were no schema changes...

operations = [
    migrations.AlterField(
        model_name='mymodel',
        name='oldname',
        field=models.BooleanField(default=False, db_column=b'oldname'),
    ),
    migrations.RenameField(
        model_name='mymodel',
        old_name='oldname',
        new_name='newname',
    ),
]

It is quite easy to fix. But you will have to modify the migration yourself.

Instead of dropping and adding the column, use db.rename_column. You can simply modify the migration created by schemamigration --auto

Actually with Django 1.10, just renaming the field in the model and then running makemigrations, immediately identifies the operation (ie. one field disappeared, another appeared in its stead):

$ ./manage.py makemigrations
Did you rename articlerequest.update_at to articlerequest.updated_at (a DateTimeField)? [y/N] y
Migrations for 'article_requests':
  article_requests/migrations/0003_auto_20160906_1623.py:
    - Rename field update_at on articlerequest to updated_at
Louis

I've run into this situation. I wanted to change the field names in the model but keep the column names the same.

The way I've done it is to do schemamigration --empty [app] [some good name for the migration]. The problem is that as far as South is concerned, changing the field names in the model is a change that it needs to handle. So a migration has to be created. However, we know there is nothing that has to be done on the database side. So an empty migration avoids doing unnecessary operation on the database and yet satisfies South's need to handle what it considers to be a change.

Note that if you use loaddata or use Django's test fixture facility (which uses loaddata behind the scenes). You'll have to update the fixtures to use the new field name because the fixtures are based on the model field names, not the database field names.

For cases where column names do change in the database, I never recommend the use db.rename_column for column migrations. I use the method described by sjh in this answer:

I have added the new column as one schemamigration, then created a datamigration to move values into the new field, then a second schemamigration to remove the old column

As I've noted in a comment on that question, the problem with db.rename_column is that it does not rename constraints together with the column. Whether the issue is merely cosmetic or whether this mean a future migration may fail because it cannot find a constraint is unknown to me.

UPDATE

tested it with Django 2.0.9, it can automatically detect if a field was renamed and gives an option to rename instead of delete and create new one

Initial

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.

I ran into this situation on Django 1.7.7. I ended up doing the following which worked for me.

./manage.py makemigrations <app_name> --empty

Added a simple subclass of migrations.RenameField that doesn't touch the database:

class RenameFieldKeepDatabaseColumn(migrations.RenameField):
def database_backwards(self, app_label, schema_editor, from_state, to_state):
    pass

def database_forwards(self, app_label, schema_editor, from_state, to_state):
    pass

It is possible to rename a field without doing any manual migration file editing:

  1. Add db_column=OLD_FIELD_NAME to the original field.
  2. Run: python3 manage.py makemigrations
  3. Rename the field from OLD_FIELD_NAME to NEW_FIELD_NAME
  4. Run: python3 manage.py makemigrations

You will be prompted:

Did you rename MODEL.OLD_FIELD_NAME to MODEL.NEW_FIELD_NAME (a ForeignKey)? [y/N] y

This will generate two migration files rather than just one, although both migrations are auto-generated.

This procedure works on Django 1.7+.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!