142

我一直在 South 的网站、Google 和 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)

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

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

  1. 我可能会进行数据迁移,但这似乎涉及很多。
  2. 编写自定义迁移,例如db.rename_table('city_citystate', 'geo_citystate'),但我不确定在这种情况下如何修复外键。
  3. 你知道更简单的方法吗?
4

4 回答 4

131

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

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(更新 2:尽量--auto避免--empty下面的警告。感谢@KFB 的提示。)

如果您使用的是旧版本的 south,则需要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 选项更简单地完成此操作。但是每次你这样做时,都会增加代码库的遗留权重——类名与表名不同会使你的代码更难理解和维护。为了清楚起见,我完全支持像这样进行简单的重构。

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

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

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

于 2010-05-24T18:07:53.733 回答
67

进行更改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新的模型名称创建一个新的。但如果有数据库对象指向它(例如,通过 a ) ,最好只update使用您已经拥有的。ContentTypeGenericForeignKey

此外,如果您已将某些列重命名为重命名模型的外键,请不要忘记

db.rename_column(myapp_model, foo_id, bar_id)
于 2012-09-11T00:21:58.533 回答
5

南方自己做不到——它怎么知道那Bar代表着Foo过去的样子?这就是我要为其编写自定义迁移的东西。您可以像上面所做的那样更改您的ForeignKeyin 代码,然后只需重命名相应的字段和表,您可以按照自己的方式进行操作。

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

于 2010-05-19T05:25:08.083 回答
-1

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

刚刚意识到,可以首先从更改模型名称开始,然后在应用它们之前编辑迁移文件。干净多了。

于 2014-12-18T21:14:04.337 回答