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

Easiest way to rename a model using Django/South?

我一直在South的网站google上寻找答案,但找不到一个简单的方法来解决这个问题。

我想用south重命名一个django模型。假设您有以下内容:

1
2
3
4
5
6
class Foo(models.Model):
    name = models.CharField()

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

你想把foo转换成bar,也就是

1
2
3
4
5
6
class Bar(models.Model):
    name = models.CharField()

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

简单来说,我只是想把名字从Foo改成Bar,但暂时忽略FooTwo中的Foo成员。

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

  • 我可能会做一个数据迁移,但这看起来相当复杂。
  • 编写一个自定义迁移,例如db.rename_table('city_citystate', 'geo_citystate'),但我不确定在这种情况下如何修复外键。
  • 你知道一个更简单的方法吗?

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

    1
    ./manage.py schemamigration yourapp rename_foo_to_bar --empty

    (更新2:尝试使用--auto而不是--empty,以避免出现以下警告。感谢@kfb提供的小费。)

    如果您使用的是较旧版本的South,则需要使用startmigration,而不是schemamigration

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

    1
    2
    3
    4
    5
    6
    7
    8
    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元选项来实现这一点。但是每次这样做,都会增加代码库的遗留权重——类名与表名不同会使代码更难理解和维护。为了清晰起见,我完全支持这样的简单重构。

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

    1
    2
    3
    4
    5
    6
    7
    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'.

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


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

    1
    ./manage.py schemamigration --auto myapp

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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):
            ...

    这不是你想要的。相反,编辑迁移,使其看起来像:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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。但最好是只使用updateContentType,以防数据库对象指向它(例如,通过GenericForeignKey)。

    另外,如果您重命名了一些列,这些列是重命名模型的外键,请不要忘记

    1
    db.rename_column(myapp_model, foo_id, bar_id)


    南方自己做不到——它怎么知道Bar代表了Foo过去的样子?这就是我要为其编写自定义迁移的类型。您可以像上面所做的那样在代码中更改您的ForeignKey,然后只需要重命名适当的字段和表,您可以用任何方式进行。

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


    我遵循上面利奥波德的解决方案。但是,这并没有改变模型的名称。我在代码中手动更改了它(也在相关模型中,这里称为fk)。做了另一次南方移民,但有——假选择。这使得模型名和表名相同。

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