216

I would like to change a name of specific fields in a model:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

should change to:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

What's the easiest way to do this using South?

Serjik
  • 10,543
  • 8
  • 61
  • 70
Jonathan Livni
  • 101,334
  • 104
  • 266
  • 359
  • 7
    See also http://stackoverflow.com/questions/2862979/easiest-way-to-rename-a-model-using-django-south for renaming a *model* rather than a *model field*. – Mechanical snail Sep 29 '11 at 04:31

6 Answers6

232

You can use the db.rename_column function.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

The first argument of db.rename_column is the table name, so it's important to remember how Django creates table names:

Django automatically derives the name of the database table from the name of your model class and the app that contains it. A model's database table name is constructed by joining the model's "app label" -- the name you used in manage.py startapp -- to the model's class name, with an underscore between them.

In the case where you have a multi-worded, camel-cased model name, such as ProjectItem, the table name will be app_projectitem (i.e., an underscore will not be inserted between project and item even though they are camel-cased).

Bruno A.
  • 1,765
  • 16
  • 17
googletorp
  • 33,075
  • 15
  • 67
  • 82
  • 2
    That would work on name\full_name, but not on the relation field, right? – Jonathan Livni Jul 13 '10 at 11:20
  • I tried db.rename_column('app_foo', 'rel', 'odd_relation') and received: _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\ap_site\\#sql-1034_20' to '.\\my_app_db\\app_foo' (errno: 150)") – Jonathan Livni Jul 13 '10 at 11:46
  • Turns out that this error is because there are indexes bonding this column (django generated unique_together constraints). Any idea how to continue from here? – Jonathan Livni Jul 13 '10 at 13:36
  • 23
    IMPORTANT NOTE: if you're going to use this, make sure "app_foo" is the database table name, so for example: "mainapp_profile", "name" is the old column name of the database (not the model field name), for example: "user_id" and "full_name" would be the new name you want the column to have (again, database column and not field name). So: db.rename_column('mainapp_profile', 'user_id', 'new_user_id') Also, if you're dealing with foreign keys, you should have the "_id" part in them when renaming. – Gezim Jul 16 '10 at 14:16
  • 3
    If you manually do this db.rename_column you may have to do a fake schemamigration afterwards to clean things back up. That is first migrate the change with renaming the columns. Then fix the model (to have the updated name), and then do ./manage.py schemamigration app --auto && ./manage.py migrate app --fake). – dr jimbob Jan 03 '11 at 21:57
  • 24
    You can also use ./manage.py schemamigration my_app renaming_column_x --empty to create an empty migration and just to place the code in it – Ilian Iliev Jul 07 '11 at 09:06
  • @Dave: no, this is not one of [South's supported autodetections](http://south.aeracode.org/docs/autodetector.html#supported-actions). I just tested on South 0.7.3 and South thinks that the old column name has been deleted, and that the new column name has been deleted. – Carl G Oct 04 '11 at 07:08
  • http://south.aeracode.org/ticket/158 interesting comment on editing the dict after renaming – Ale Nov 12 '12 at 18:24
  • note: if the renamed table is part of a unique together relationship, you have to redeclare it with db.create_unique – Ciro Santilli OurBigBook.com Nov 21 '12 at 17:30
  • 11
    --empty doesn't help much, instead use --auto and modify created migration. This way it won't claim field has been deleted for next migration of models.py. – hurturk May 19 '13 at 20:00
  • It is possible to retrieve db table corresponding to a model in `DataMigration`, and rename column there. – Gill Bates Nov 11 '13 at 21:51
39

Here's what I do:

  1. Make the column name change in your model (in this example it would be myapp/models.py)
  2. Run ./manage.py schemamigration myapp renaming_column_x --auto

Note renaming_column_x can be anything you like, it's just a way of giving a descriptive name to the migration file.

This will generate you a file called myapp/migrations/000x_renaming_column_x.py which will delete your old column and add a new column.

Modify the code in this file to change the migration behaviour to a simple rename:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')
donturner
  • 17,867
  • 8
  • 59
  • 81
  • what is name of the column in your command? `x` or `column_x`? – andilabs Apr 02 '14 at 11:19
  • The part of the command you're referring to is just used to name the migration, it can be whatever you like. The column name will need to be specified in the migration file when you edit it. – donturner Apr 02 '14 at 21:31
  • 2
    Creating the `--auto` migration first is a great tip. It avoids problems with the South ORM Freezer, which occur if the migration only has `forwards` and `backwards` methods, but does not contain the frozen `model` object. – mwcz May 21 '14 at 21:29
  • How do I answer south's question 'Since you are removing this field, you MUST specify a default'? – Bryce Sep 26 '14 at 19:48
  • Might be worth asking a new question for that as the column type is important and there may be other contextual information which is needed to make the correct choice for a default value. – donturner Sep 30 '14 at 19:51
  • 3
    The one problem I have with this method is that `db.rename_column` does not rename the *constraints* associated with the column. The migration will still work but you'll have constraints named after the old column name. I had a column with a uniqueness constraint, renamed it using this method, tested that the uniqueness constraint still existed and got an error but the name of the constraint itself was still using the old column name. Perhaps an explicit `db.delete_unique` and `db.create_unique` would have done it but I decided to go with sjh's solution. – Louis Nov 03 '14 at 16:53
15

I didn't know about db.rename column, sounds handy, however in the past 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

sjh
  • 2,236
  • 1
  • 18
  • 21
  • Me too. The problem with the schema-data-schema technique is that you end up with different names for your fields/models. Sometimes this is a problem if you use canonical names to enable dispatchers... – Jonathan Livni Jul 14 '10 at 06:14
  • I just tried this and it didn't work as there was a name conflict. I had exact same FK but different name. It didn't validate. So, _don't_ do this. – Gezim Jul 15 '10 at 22:55
  • 2
    @jonathan, he wants different names!... @pilgrim, care to post some code? I have done this several times this week, if your models don't validate then south wont create the migrations. – sjh Jul 16 '10 at 07:56
  • That would work, but would probably be slower than the 'rename_column' that other people have suggested. I know MySQL can do a "ALTER TABLE ... rename column" (or whatever it is) quite quickly. – Amandasaurus Jul 18 '11 at 10:01
  • 1
    @Rory `db.rename_column` won't rename constraints for you so you have to handle that manually. If you *forget* to do it, the migration will work except that unbeknownst to you there may be a constraint still using the old column name. It is unclear to me whether the issue is merely cosmetic or whether in a future migration where the constraint should be manipulated or dropped South won't be able to find it. At any rate, doing it here like sjh suggests is the safe way to do it: you can let South figure out what it should be figuring out. – Louis Nov 03 '14 at 17:00
12

Django 1.7 introduced Migrations so now you don't even need to install extra package to manage your migrations.

To rename your model you need to create empty migration first:

$ manage.py makemigrations <app_name> --empty

Then you need to edit your migration's code like this:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

And after that you need to run:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
  • 5,053
  • 7
  • 43
  • 69
7

Just change the model and run makemigrations in 1.9

Django automatically detects that you've deleted and created a single field, and asks:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Say yes, and the right migration gets created. Magic.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
0
  1. Add south to your installed apps in project setting file.
  2. Comment out the added/modified field/table.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Un-comment the field and write the modified one
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

If you are using 'pycharm', then you can use 'ctrl+shift+r' instead of 'manage.py' , and 'shift ' for parameters.

Chillar Anand
  • 27,936
  • 9
  • 119
  • 136
ancho
  • 1,060
  • 16
  • 24