Easiest way to rename a model using Django/South?

后端 未结 4 1788
無奈伤痛
無奈伤痛 2020-11-29 15:44

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.

相关标签:
4条回答
  • 2020-11-29 15:50

    Make the changes in models.py and then run

    ./manage.py schemamigration --auto myapp
    

    When you inspect the migration file, you'll see that it deletes a table and creates a new one

    class Migration(SchemaMigration):
    
        def forwards(self, orm):
            # Deleting model 'Foo'                                                                                                                      
            db.delete_table('myapp_foo')
    
            # Adding model 'Bar'                                                                                                                        
            db.create_table('myapp_bar', (
            ...
            ))
            db.send_create_signal('myapp', ['Bar'])
    
        def backwards(self, orm):
            ...
    

    This is not quite what you want. Instead, edit the migration so that it looks like:

    class Migration(SchemaMigration):
    
        def forwards(self, orm):
            # Renaming model from 'Foo' to 'Bar'                                                                                                                      
            db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
            if not db.dry_run:
                orm['contenttypes.contenttype'].objects.filter(
                    app_label='myapp', model='foo').update(model='bar')
    
        def backwards(self, orm):
            # Renaming model from 'Bar' to 'Foo'                                                                                                                      
            db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
            if not db.dry_run:
                orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')
    

    In the absence of the update statement, the db.send_create_signal call will create a new ContentType with the new model name. But it's better to just update the ContentType you already have in case there are database objects pointing to it (e.g., via a GenericForeignKey).

    Also, if you've renamed some columns which are foreign keys to the renamed model, don't forget to

    db.rename_column(myapp_model, foo_id, bar_id)
    
    0 讨论(0)
  • 2020-11-29 15:59

    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.

    0 讨论(0)
  • 2020-11-29 15:59

    I followed Leopd's solution above. But, that did not change the model names. I changed it manually in the code (also in related models where this is referred as FK). And done another south migration, but with --fake option. This makes model names and table names to be same.

    Just realized, one could first start with changing model names, then edit the migrations file before applying them. Much cleaner.

    0 讨论(0)
  • 2020-11-29 16:04

    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.

    0 讨论(0)
提交回复
热议问题