我一直在 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'),但我不确定在这种情况下如何修复外键。你知道更简单的方法吗?
要回答您的第一个问题,简单的模型/表重命名非常简单。运行命令:
./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 选项更简单地完成此操作。但是每次你这样做时,都会增加代码库的遗留权重——类名与表名不同会使你的代码更难理解和维护。为了清楚起见,我完全支持像这样进行简单的重构。
(更新)我刚刚在生产中尝试过这个,当我去应用迁移时收到了一个奇怪的警告。它说:
以下内容类型已过时,需要删除:yourapp | foo 通过外键与这些内容类型相关的任何对象也将被删除。您确定要删除这些内容类型吗?如果您不确定,请回答“否”。
我回答“不”,一切似乎都很好。
在 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)
./manage.py datamigration
的 --frozen
标志将 contenttypes.ContentType
模型添加到冻结模型来解决此问题。例如:./manage.py datamigration --frozen contenttypes myapp update_contenttypes
。然后使用上面指定的内容类型更新代码编辑 myapp_migrations/NNNN_update_contenttypes.py。
South 自己做不到 - 它怎么知道 Bar
代表 Foo
过去的样子?这就是我要为其编写自定义迁移的东西。您可以像上面那样在代码中更改您的 ForeignKey
,然后只需重命名相应的字段和表,您可以按照自己的方式进行操作。
最后,你真的需要这样做吗?我还不需要重命名模型 - 模型名称只是一个实现细节 - 特别是考虑到 verbose_name
Meta 选项的可用性。
db_table
Meta 选项保持数据库表名相同。
db_table
是否用于派生外键名称吗?
我遵循了上面 Leopd 的解决方案。但是,这并没有改变模型名称。我在代码中手动更改了它(也在被称为 FK 的相关模型中)。并进行了另一次南迁,但使用了 --fake 选项。这使得模型名称和表名称相同。
刚刚意识到,可以先从更改模型名称开始,然后在应用它们之前编辑迁移文件。干净多了。