首页 文章

使用Django / South重命名模型的最简单方法?

提问于
浏览
138

我一直在寻找南方网站,谷歌和SO的答案,但找不到一个简单的方法来做到这一点 .

我想用South重命名一个Django模型 . 说你有以下内容:

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

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

并且你想将Foo转换为Bar,即

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

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

为了简单起见,我只是想将名称从 Foo 更改为 Bar ,但暂时忽略 FooTwo 中的 foo 成员 .

使用南方最简单的方法是什么?

  • 我可能会进行数据迁移,但这似乎非常复杂 .

  • 编写自定义迁移,例如 db.rename_table('city_citystate', 'geo_citystate') ,但我不确定在这种情况下如何修复外键 .

  • 您知道的更简单的方法吗?

4 回答

  • 66

    要回答您的第一个问题,简单的模型/表重命名非常简单 . 运行命令:

    ./manage.py schemamigration yourapp rename_foo_to_bar --empty
    

    (更新2:尝试 --auto 而不是 --empty 以避免下面的警告 . 感谢@KFB提示 . )

    如果你需要 startmigration 而不是 schemamigration .

    然后手动编辑迁移文件,如下所示:

    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')
    

    您可以使用模型类中的 db_table Meta选项更简单地完成此操作 . 但每次执行此操作时,都会增加代码库的遗留权重 - 类名与表名不同会使代码难以理解和维护 . 为了清楚起见,我完全支持像这样做简单的重构 .

    (更新)我刚刚在 生产环境 中尝试了这个,并且在我去应用迁移时收到了一个奇怪的警告 . 它说:

    以下内容类型陈旧且需要删除:

    yourapp | FOO

    通过外键与这些内容类型相关的任何对象也将
    被删除 . 您确定要删除这些内容类型吗?
    如果您不确定,请回答“否” .

    我回答“不”,一切似乎都很好 .

  • -1

    models.py 中进行更改,然后运行

    ./manage.py schemamigration --auto myapp
    

    检查迁移文件时,您将看到它删除了一个表并创建了一个新表

    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):
            ...
    

    这不是你想要的 . 而是编辑迁移,使其看起来像:

    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')
    

    如果没有 update 语句, db.send_create_signal 调用将使用新的模型名称创建新的 ContentType . 但是,如果存在指向它的数据库对象(例如,通过 GenericForeignKey ),那么最好只使用 update .

    此外,如果您重命名了一些作为重命名模型的外键的列,请不要忘记

    db.rename_column(myapp_model, foo_id, bar_id)
    
  • 128

    南方不能自己做 - 它怎么知道 Bar 代表了什么 Foo 曾经?这是我为自己编写自定义迁移的事情 . 您可以在代码中更改 ForeignKey ,只需重命名相应的字段和表格即可,您可以按照自己的方式进行操作 .

    最后,你真的需要这样做吗?我还需要重命名模型 - 模型名称只是一个实现细节 - 特别是考虑到verbose_name Meta选项的可用性 .

  • 5

    我按照上面的Leopd解决方案 . 但是,这并没有改变模型名称 . 我在代码中手动更改了它(也在相关模型中,它被称为FK) . 并完成了另一次南迁,但有--fake选项 . 这使得模型名称和表名称相同 .

    刚刚意识到,可以先从更改模型名称开始,然后在应用它们之前编辑迁移文件 . 更清洁 .

相关问题