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

我一直在 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成员。

用南方最简单的方法是什么?

  1. 我可能会做一个数据迁移,但这似乎相当复杂。
  2. 编写一个自定义迁移,例如 db.rename_table('city_citystate', 'geo_citystate'),但是我不确定在这种情况下如何修复外键。
  3. 你知道更简单的方法吗?
30664 次浏览

南方不能自己做到这一点-它怎么知道 Bar代表什么 Foo过去?这就是我要为之编写自定义迁移的事情。您可以像上面所做的那样在代码中更改 ForeignKey,然后只需要重命名适当的字段和表即可,您可以按照自己的意愿进行重命名。

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

要回答第一个问题,简单的 model/table 重命名非常简单:

./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 选项可以更简单地实现这一点。但是每次这样做时,都会增加代码库的遗留权重——类名与表名不同会使代码更难理解和维护。为了清晰起见,我完全支持进行这样的简单重构。

(update)我刚刚在生产环境中尝试了这种方法,在应用迁移时得到了一个奇怪的警告。它表示:

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

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

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)

我遵循了利奥波德的上述解决方案。但是,这并没有改变模型的名称。我在代码中手动更改了它(在相关模型中也是如此,这被称为 FK)。再往南迁徙一次,但是... 假的选择。这使得模型名称和表名称相同。

刚刚意识到,可以首先更改模型名称,然后在应用迁移文件之前编辑它们。