我计划重命名现有 Django 项目中的多个模型,其中有许多其他模型与我要重命名的模型具有外键关系。我相当确定这将需要多次迁移,但我不确定确切的过程。

假设我从 Django 应用程序中的以下模型开始,名为 myapp:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

我想重命名 Foo 模型,因为该名称实际上没有意义并且会导致代码混乱,并且 Bar 会让一个更清晰的名字。

根据我在 Django 开发文档中阅读的内容,我假设以下迁移策略:

步骤1

调整 models.py:

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_ridonkulous = models.BooleanField()

请注意 AnotherModel 的字段名称 foo 没有改变,但是关系更新为 Bar 模型。我的理由是,我不应该立即更改太多,并且如果我将此字段名称更改为 bar 我可能会冒丢失该列中数据的风险。

第2步

创建一个空的迁移:

python manage.py makemigrations --empty myapp

步骤3

编辑 Migration 在步骤 2 中创建的迁移文件中添加 RenameModel 操作到操作列表:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

步骤4

应用迁移:

python manage.py migrate

步骤5

编辑相关字段名称 models.py:

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

步骤6

创建另一个空迁移:

python manage.py makemigrations --empty myapp

步骤7

编辑 Migration 类在步骤 6 中创建的迁移文件中添加 RenameField 操作列表中任何相关字段名称的操作:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_rename_fields'),  # <-- is this okay?
    ]

    operations = [
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

步骤8

应用第二次迁移:

python manage.py migrate

除了更新其余代码(视图、表单等)以反映新变量名称之外,这基本上是新迁移功能的工作方式吗?

另外,这似乎有很多步骤。迁移操作可以以某种方式压缩吗?

谢谢!

有帮助吗?

解决方案

所以当我尝试这个时,似乎你可以压缩步骤 3 - 7:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'), 
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar'),
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

如果您不更新导入的名称,您可能会收到一些错误,例如admin.py 甚至更旧的迁移文件(!)。

更新: :作为 塞萨罗 提到,较新版本的 Django 通常能够检测并询问模型是否被重命名。所以尝试一下 manage.py makemigrations 首先,然后检查迁移文件。

其他提示

起初,我认为 Fiver 的方法对我有用,因为迁移在步骤 4 之前都运行良好。但是,将“ForeignKeyField(Foo)”隐式更改为“ForeignKeyField(Bar)”与任何迁移都不相关。这就是当我想重命名关系字段时迁移失败的原因(步骤 5-8)。这可能是由于我的“AnotherModel”和“YetAnotherModel”在我的情况下在其他应用程序中调度。

因此,我设法重命名我的模型和关系字段,执行以下步骤:

我改编了该方法 尤其是奥特兰泽的技巧。

就像 Fiver 一样,我们假设我们有 我的应用程序:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

并且在 我的其他应用程序:

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

步骤1:

将每个 OneToOneField(Foo) 或foreignKeyField(Foo) 转换为IntegerField()。(这会将相关 Foo 对象的 id 保留为整数字段的值)。

class AnotherModel(models.Model):
    foo = models.IntegerField()
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.IntegerField()
    is_ridonkulous = models.BooleanField()

然后

python manage.py makemigrations

python manage.py migrate

第2步:(如 Fiver 的步骤 2-4)

更改型号名称

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

创建一个空的迁移:

python manage.py makemigrations --empty myapp

然后编辑它,如下所示:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

最终

python manage.py migrate

步骤3:

将您的 IntegerField() 转换回之前的foreignkeyfield或onetoonefield,但使用新的bar模型。(前面的整数字段存储了 id,因此 django 理解这一点并重新建立连接,这很酷。)

class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_ridonkulous = models.BooleanField()

然后做:

python manage.py makemigrations 

非常重要的是,在此步骤中,您必须修改每个新迁移并添加对 RenameModel Foo-> Bar 迁移的依赖项。因此,如果 AnotherModel 和 YetAnotherModel 都在 myotherapp 中,则 myotherapp 中创建的迁移必须如下所示:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
        ('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
    ]

    operations = [
        migrations.AlterField(
            model_name='anothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar'),
        ),
        migrations.AlterField(
            model_name='yetanothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar')
        ),
    ]

然后

python manage.py migrate

步骤4:

最终你可以重命名你的字段

class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_ridonkulous = models.BooleanField()

然后进行自动重命名

python manage.py makemigrations

(django 应该询问你是否真的重命名了模型名称,回答是)

python manage.py migrate

就是这样!

这适用于 Django1.8

我需要做同样的事情。我一次性更改了模型(即步骤 1 和步骤 5 一起更改)。然后创建了一个架构迁移,但将其编辑为:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')

这非常有效。我所有现有的数据都显示出来了,所有其他表都引用了 Bar 很好。

从这里: https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/

对于 Django 1.10,我通过简单地运行 Makemigrations,然后运行应用程序的 Migrate 来更改两个模型类名称(包括外键和数据)。对于 Makemigrations 步骤,我必须确认我想要更改表名称。迁移更改表的名称没有出现问题。

然后我更改了ForeignKey字段的名称以匹配,并且Makemigrations再次要求我确认我想要更改名称。迁移而不是做出改变。

所以我分两步完成了这个任务,没有任何特殊的文件编辑。正如 @wasibigeek 所提到的,我一开始确实遇到了错误,因为我忘记更改 admin.py 文件。

我也遇到了 v.thorey 描述的问题,发现他的方法非常有用,但可以压缩为更少的步骤,实际上是步骤 5 到 8,如 Fiver 所描述的,没有步骤 1 到 4,除了步骤 7 需要更改为我的下面的步骤 3。总体步骤如下:

步骤1:编辑models.py中相关字段名称

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

第2步:创建空迁移

python manage.py makemigrations --empty myapp

步骤3:编辑步骤 2 中创建的迁移文件中的迁移类

class Migration(migrations.Migration):

dependencies = [
    ('myapp', '0001_initial'), 
]

operations = [
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.RenameModel('Foo', 'Bar'),
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.RenameField('AnotherModel', 'foo', 'bar'),
    migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]

步骤4:应用迁移

python manage.py migrate

完毕

附:我在 Django 1.9 上尝试过这种方法

我使用的是 Django 版本 1.9.4

我已按照以下步骤操作:-

我刚刚将模型旧名称重命名为newname run python manage.py makemigrations. 。它会询问你Did you rename the appname.oldName model to NewName? [y/N] 选择Y

跑步 python manage.py migrate 它会要求你

以下内容类型已过时,需要删除:

appname | oldName
appname | NewName

通过外键与这些内容类型相关的任何对象也将被删除。您确定要删除这些内容类型吗?如果您不确定,请回答“否”。

Type 'yes' to continue, or 'no' to cancel: Select No

它为我重命名所有现有数据并将其迁移到新的命名表。

不幸的是,我发现重命名迁移存在问题(每个 django 1.x),这会在数据库中留下旧的表名。

Django 甚至没有在旧表上尝试任何东西,只是重命名他自己的模型。外键和索引也存在同样的问题 - Django 无法正确跟踪其中的更改。

最简单的解决方案(解决方法):

class Foo(models.Model):
     name = models.CharField(unique=True, max_length=32)
     ...
Bar = Foo  # and use Bar only

真正的解决方案(一种简单的方法,可以在2个提交中切换所有索引,约束,触发器,名称等,而是用于 较小 表):

提交A:

  1. 创建 相同的 模型与旧模型一样
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. 切换代码以适用于新模型 Bar 仅有的。(包括模式上的所有关系)

迁移准备中 RunPython, ,哪些将数据从foo复制到bar(包括 id 福)

  1. 可选优化(如果需要更大的表)

提交B: (不着急,当整个团队迁移时再做)

  1. 旧型号的安全掉落 Foo

进一步清理:

  • 抑制移民

Django 中的错误:

我需要重命名几个表。但 Django 只注意到了一个模型重命名。发生这种情况是因为 Django 迭代添加然后删除的模型。对于每一对,它都会检查它们是否属于同一应用程序并且具有 相同的字段. 。只有一张表没有要重命名的表的外键(正如您所记得的,外键包含模型类名称)。换句话说,只有一张表没有字段变化。这就是它被注意到的原因。

因此,解决方案是一次重命名一个表,更改模型类名称 models.py, 可能 views.py, ,并进行迁移。然后检查代码中的其他引用(模型类名称、相关(查询)名称、变量名称)。如果需要,进行迁移。然后,可以选择将所有这些迁移合并为一个(确保也复制导入)。

我会说@ceasaro的话,我对他对此的评论 回答.

较新版本的 Django 可以检测更改并询问做了什么。我还要补充一点,Django 可能会混合某些迁移命令的执行顺序。

明智的做法是应用小的更改并运行 makemigrationsmigrate 如果发生错误,可以编辑迁移文件。

可以更改某些行的执行顺序以避免错误。

只是想确认并补充塞萨罗的评论。Django 2.0 现在似乎自动执行此操作。

我使用的是 Jango 2.2.1,我所要做的就是重命名模型并运行 makemigrations。

这里它询问我是否已将特定类从 A 重命名为 B,我选择了“是”并运行 migrate,一切似乎都有效。

注意我没有重命名项目/迁移文件夹内的任何文件中的旧模型名称。

如果您使用的是 PyCharm 等优秀的 IDE,您可以右键单击模型名称并执行重构 -> 重命名。这节省了您浏览所有引用该模型的代码的麻烦。然后运行 ​​makemigrations 并迁移。Django 2+ 将简单地确认名称更改。

我将 Django 从版本 10 升级到版本 11:

sudo pip install -U Django

(-U 对于“升级”)它解决了问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top