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

Posted

技术标签:

【中文标题】使用 Django/South 重命名模型的最简单方法?【英文标题】:Easiest way to rename a model using Django/South? 【发布时间】:2011-02-21 05:20:01 【问题描述】:

我一直在 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)

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

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

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

【问题讨论】:

另见 ***.com/questions/3235995/… 以重命名 model 字段 而不是 model Django 优化解决方案 >= 1.8 ***.com/questions/25091130/… 【参考方案1】:

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

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

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

【讨论】:

我能够通过使用 --auto 而不是 --empty 创建模式迁移来避免 Leopd 的错误消息。然后我编辑了迁移文件,将表的删除/创建更改为 db.rename_table() 调用。这似乎效果很好。 我在 2011 年 9 月 2 日使用了这种技术,没有出现任何错误。也许较新版本的 South 解决了错误问题。 感谢您保持更新!简在下面的回答说保持“send_create_signal”调用很重要,您对此有任何了解吗?如果您同意,最好更新您的示例迁移。 请注意,这不会重命名该表上的索引。如果您将来创建一个与旧表同名的新表,您可能会因索引名称冲突而出错。我们使用了这种技术,但从现在开始,我们将显式创建新表,迁移数据,然后删除旧表。 自动生成的表中的列名,例如M2M表到原始模型,也不会通过这种方法迁移。【参考方案2】:

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。但最好只使用 update 和已有的 ContentType,以防有数据库对象指向它(例如,通过 GenericForeignKey)。

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

db.rename_column(myapp_model, foo_id, bar_id)

【讨论】:

我收到错误,KeyError:“来自应用程序 'contenttypes' 的模型 'contenttype' 在此迁移中不可用。”另外,我有一个 django_content_type 表,但没有 contenttypes 表。 (Django 1.6) @Seth 我通过在单独的数据迁移中更新 ContentType 模型并通过使用 --frozen 标志到 ./manage.py datamigrationcontenttypes.ContentType 模型添加到冻结模型来解决这个问题.例如:./manage.py datamigration --frozen contenttypes myapp update_contenttypes。然后使用上面指定的内容类型更新代码编辑 myapp_migrations/NNNN_update_contenttypes.py。 @GeoffreyHing 我认为参数是冻结没有冻结。 south.readthedocs.io/en/latest/ormfreezing.html 不过非常感谢你的帮助,真的很有帮助。【参考方案3】:

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

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

【讨论】:

或者,在代码中重命名模型,但使用db_table Meta 选项保持数据库表名相同。 @Daniel - 你知道db_table 是否用于派生外键名称吗? 我相信它是。如果您更改模型名称并设置 db_table 一切仍应按预期工作。 @DanielRoseman 这是整个线程中最好的解决方案!【参考方案4】:

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

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

【讨论】:

以上是关于使用 Django/South 重命名模型的最简单方法?的主要内容,如果未能解决你的问题,请参考以下文章

django south 在多个数据库上

Django South:为多个应用程序创建架构迁移

什么是 Django South GhostMigrations 异常以及如何调试它?

如何对 Django South“数据迁移”进行单元测试

运行单元测试时禁用 Django South?

巩固 Django South Migrations