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

143

我曾在 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最简单的方法是什么?

  1. 我可能可以进行数据迁移,但这似乎相当复杂。
  2. 编写自定义迁移,例如 db.rename_table('city_citystate', 'geo_citystate'),但我不确定如何在此情况下修复外键。
  3. 您知道的更简单的方法吗?

5
请参考https://dev59.com/PHA75IYBdhLWcg3wi5sr,了解如何重命名*模型字段*而不是*模型*。 - Mechanical snail
Django >= 1.8 的优化解决方案 https://dev59.com/El8f5IYBdhLWcg3wD_Av - Chemical Programmer
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元选项来实现这一点。但是每次这样做,都会增加代码库的遗留权重——类名与表名不同使您的代码更难理解和维护。我完全支持出于清晰度的考虑进行简单的重构。

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

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

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


3
我通过使用 --auto 而非 --empty 来创建模式迁移,成功避免了Leopd的错误信息。然后我编辑了迁移文件,将表的删除/创建更改为 db.rename_table() 调用。这似乎非常成功。 - KFB
4
我在2011年9月2日使用了这种技术,没有出现任何错误。也许更新的South版本解决了错误问题。 - Brian Tol
5
请注意,这不会重命名该表上的索引。如果您未来创建一个与旧表同名的新表,可能会出现索引名称冲突的错误。我们曾经使用过这种技术,但从现在开始,我们将明确创建新表,迁移数据,然后删除旧表。 - Jeremy
3
自动生成的表格中的列名,例如M2M表格到原始模型,也不会通过此方法迁移。 - spookylukey
1
这个答案有多久了?它适用于Django 1.8吗?我在可用的Django管理命令列表中没有看到schemamigration... - TheGrimmScientist
显示剩余9条评论

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。但是最好只更新已有的ContentType,以防止数据库中存在指向它的对象(例如通过GenericForeignKey)。此外,如果您已经重命名了一些作为外键引用的列的模型,请不要忘记进行更新操作。
db.rename_column(myapp_model, foo_id, bar_id)

2
我遇到了错误,KeyError: "应用程序 'contenttypes' 中的模型 'contenttype' 在此迁移中不可用。" 此外,我有一个 django_content_type 表,但没有 contenttypes 表。(Django 1.6) - Seth
2
@Seth 我通过在单独的数据迁移中更新ContentType模型,并使用--frozen标志将contenttypes.ContentType模型添加到冻结模型中来解决了这个问题。例如:./manage.py datamigration --frozen contenttypes myapp update_contenttypes。然后按照上面指定的内容类型更新代码编辑myapp_migrations/NNNN_update_contenttypes.py。 - Geoffrey Hing
@GeoffreyHing,我认为参数应该是freeze而不是frozen。http://south.readthedocs.io/en/latest/ormfreezing.html 但是非常感谢您的帮助,真的很有用。 - ccsakuweb

5
南方无法单独完成此项任务 - 它如何知道Bar代表了以前的Foo?这是我会为其编写自定义迁移的类型。您可以像上面所做的那样在代码中更改ForeignKey,然后只需重命名相应的字段和表格即可,您可以按任何方式进行操作。
最后,您真的需要这样做吗?我还没有必要重命名模型 - 模型名称只是一种实现细节 - 特别是考虑到verbose_name Meta选项的可用性。

7
可以在代码中重命名模型,但使用 db_table 元选项来保持数据库表名不变。 - Daniel Roseman
@Daniel - 你知道db_table是否用于推导外键名吗? - Dominic Rodger
我相信是这样的。如果你改变了模型的名称并设置了 db_table,那么一切都应该如预期般正常工作。 - Davor Lucic
1
@DanielRoseman 这是整个线程中最好的解决方案! - joerick

-1

我按照Leopd上面的解决方案进行了操作。但是,这并没有改变模型名称。我在代码中手动更改了它(还有相关模型中引用它作为FK的地方)。然后进行了另一个south迁移,但使用了--fake选项。这使得模型名称和表名称相同。

刚刚意识到,可以先更改模型名称,然后再编辑迁移文件,然后再应用它们。更加清晰明了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接