views:

522

answers:

3

Hi everyone, I've been hunting for an answer to this on South's site, google, and SO, but couldn't find a simple way to do this.

I want to rename a Django model using South. Say you have the following:

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

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

and you want to convert Foo to Bar, namely

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

To keep it simple, I'm just trying to change the name from Foo to Bar, but ignore the 'foo' member in FooTwo for now.

What's the easiest way to do this using South?
a) I could probably do a data migration, but that seems pretty involved.
b) Write a custom migration, e.g. db.rename_table('city_citystate', 'geo_citystate'), but I'm not sure how to fix the foreign key in this case.
c) An easier way that you know?

Thanks!

+2  A: 

South can't do it itself - how does it know that Bar represents what Foo used to? This is the sort of thing I'd write a custom migration for. You can change your ForeignKey in code as you've done above, and then it's just a case of renaming the appropriate fields and tables, which you can do any way you want.

Finally, do you really need to do this? I've yet to need to rename models - model names are just an implementation detail - particularly given the availability of the verbose_name Meta option.

Dominic Rodger
Alternatively, rename the model in code but use the `db_table` Meta option to keep the database table name the same.
Daniel Roseman
@Daniel - do you know if `db_table` is used to derive foreign key names?
Dominic Rodger
@Dominic i belive it is. If you change the model name and set db_table everything should still work as expected.
rebus
+3  A: 

To answer your first question, the simple model / table rename is pretty straightforward. Run the command

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Update 2: try --auto instead of --empty to avoid the warning below. Thanks to @KFB for the tip.)

If you're using an older version of south, you'll need startmigration instead of schemamigration.

Then manually edit the migration file to look like this:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

You can accomplish this more simply using the db_table Meta option in your model class. But every time you do that, you increase the legacy weight of your codebase -- having class names differ from table names makes your code harder to understand and maintain. I fully support doing simple refactorings like this for the sake of clarity.

(update) I just tried this in production, and got a strange warning when I went to apply the migration. It said

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will

also be deleted. Are you sure you want to delete these content types? If you're unsure, answer 'no'.

I answered "no" and everything seemed to be fine.

Leopd
+1  A: 

I was able to avoid the error message that Leopd by creating the schema migration using --auto instead of --empty. I then edited the migration file, changing the deletion/creation of the tables into a db.rename_table() call. This seems to have worked very well.

KFB