使用Django/South重命名模型的最简单方法是什么?
我在South的网站、谷歌和Stack Overflow上找了很久,想找到一个简单的方法来解决这个问题。
我想用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')
,但我不太确定在这种情况下怎么处理外键。 - 你知道更简单的方法吗?
4 个回答
南方(South)不能自己完成这个任务——它怎么知道 Bar
代表的是什么 Foo
曾经代表的呢?这就是我会写一个自定义迁移的原因。你可以像上面那样在代码中更改你的 ForeignKey
,然后只需要重命名相关的字段和表,这个你可以随意处理。
最后,你真的需要这样做吗?我还没有遇到需要重命名模型的情况——模型名称只是实现的细节——尤其是考虑到有 verbose_name
这个 Meta 选项可用。
在 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
,并使用新的模型名称。但如果数据库中有对象指向这个类型(比如通过 GenericForeignKey
),最好还是更新你已经存在的 ContentType
。
另外,如果你重命名了一些作为外键的列,指向重命名后的模型,别忘了
db.rename_column(myapp_model, foo_id, bar_id)
针对你的第一个问题,简单的模型/表重命名其实很简单。你只需要运行这个命令:
./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'.
我回答“否”,然后一切似乎都没问题。