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


141

我一直在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. 您知道一种更简单的方法吗?

5
另请参阅stackoverflow.com/questions/3235995/…,以重命名模型字段而不是模型
机械蜗牛

适用于Django> = 1.8优化的解决方案stackoverflow.com/questions/25091130/...
化学程序员

Answers:


130

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

./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选项来更简单地完成此操作。但是每次这样做,都增加了代码库的旧版权重-类名与表名不同会使代码难以理解和维护。为了清楚起见,我完全支持进行这样的简单重构。

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

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 2010年

4
我在2011年9月2日使用了此技术,没有出现任何错误。也许是较新版本的South解决了带有错误的问题。
Chip Tol

1
感谢您保持更新!Jian在下面的回答中说,保持“ send_create_signal”调用很重要,您对此有任何了解吗?如果您同意,最好更新示例迁移。
mrooney 2012年

5
请注意,这不会重命名该表上的索引。如果将来创建与旧表同名的新表,则索引名冲突会导致错误。我们一直在使用这种技术,但是从现在开始,我们将显式创建新表,迁移数据,然后删除旧表。
杰里米·班克斯

3
此方法也不会迁移自动生成的表(例如M2M表到原始模型)中的列名。
spookylukey

66

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

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

db.rename_column(myapp_model, foo_id, bar_id)

2
我收到错误消息KeyError:“应用'contenttypes'中的模型'contenttype'在此迁移中不可用。” 另外,我有django_content_type表,但没有contenttypes表。(Django 1.6)
塞思

2
@Seth我通过在单独的数据迁移中更新ContentType模型并contenttypes.ContentType通过使用--frozen标记将模型添加到冻结的模型中来解决此问题./manage.py datamigration。例如:./manage.py datamigration --frozen contenttypes myapp update_contenttypes。然后使用上面指定的内容类型更新代码编辑myapp_migrations / NNNN_update_contenttypes.py。
Geoffrey Hing 2014年

@GeoffreyHing我认为参数是不冻结的。south.readthedocs.io/en/latest/ormfreezing.html但非常感谢您的帮助,它确实很有帮助。
ccsakuweb

5

南方本身不能做-怎么知道这Bar代表Foo过去?我将为此编写自定义迁移。您可以ForeignKey像上面所做的那样更改in代码,然后只是重命名适当的字段和表的一种情况,您可以根据需要进行任何操作。

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


7
或者,在代码中重命名模型,但使用db_tableMeta选项使数据库表名称保持不变。
Daniel Roseman

@Daniel-您是否知道是否db_table用于派生外键名称?
多米尼克·罗杰

我相信它是。如果更改模型名称并设置db_table,则所有内容仍应按预期工作。
达沃·卢西奇

1
@DanielRoseman这是整个线程中最好的解决方案!
joerick 2014年

-1

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.