Django重命名模型和关系字段的迁移策略

我打算在现有的Django项目中重新命名几个模型,其中有许多其他模型与我想要重命名的模型有外键关系。 我相当肯定这将需要多次迁移,但我不确定确切的程序。

比方说,我从一个名为myapp的Django应用程序中的以下模型开始:

 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() 

请注意, fooAnotherModel字段名称不会更改,但关系会更新为Bar模型。 我的推理是,我不应该立即改变太多,如果我改变这个字段的名字,我会冒险丢失在这一栏的数据。

第2步

创build一个空的迁移:

 python manage.py makemigrations --empty myapp 

第3步

编辑在步骤2中创build的迁移文件中的Migration类,以将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步

创build另一个空迁移:

 python manage.py makemigrations --empty myapp 

第七步

在步骤6中创build的迁移文件中编辑Migration类,以将任何相关字段名称的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 

除了更新代码的其余部分(视图,表单等)以反映新的variables名称,这基本上是这个新的迁移function如何工作?

另外,这似乎是很多步骤。 迁移操作能否以某种方式被压缩?

谢谢!

所以当我尝试这个时,看起来你可以压缩步骤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甚至更早的迁移文件(!),则可能会出现一些错误

起初,我认为Fiver的方法对我很有帮助,因为直到第4步,迁移才运行良好。但是,在任何迁移中隐式更改'ForeignKeyField(Foo)'到'ForeignKeyField(Bar)'中都没有关系。 这就是为什么迁移失败时,我想重新命名关系字段(步骤5-8)。 这可能是由于我的“AnotherModel”和“YetAnotherModel”是在其他应用程序中分派的。

所以我设法重命名我的模型和关系字段按照以下步骤进行:

我改编了这个方法,特别是otranzer的把戏。

所以就像Fiver那样,我们可以在myapp中看到:

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

myothepp

 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:

将每个OntToOneField(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 

第二步:(如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) 

创build一个空的迁移:

 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 Model。 (以前的整数字段是存储的ID,所以Django明白,并重新build立连接,这是很酷。

 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中创build的迁移必须如下所示:

 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)。 然后创build一个模式迁移,但编辑它是这样的:

 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/

我还遇到了v.thorey描述的问题,并发现他的方法是非常有用的,但可以被压缩成更less的步骤,实际上是步骤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步:创build一个空的迁移

 python manage.py makemigrations --empty myapp 

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

 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 

完成

PS我在Django 1.9上试过这个方法

不幸的是,我发现问题(每个django 1.x)重命名迁移,在数据库中留下旧的表名(甚至不尝试任何旧表,只是重命名模型)。

解决scheme:

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

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

然后我改变了ForeignKey字段的名字以匹配,并且再次被Makemigrations询问以确认我想改变名字。 迁移比做了改变。

所以我采取了两个步骤,没有任何特殊的文件编辑。 我起初得到错误,因为我忘了更改admin.py文件,正如@wasibigeek所述。

我正在使用Django版本1.9.4

我遵循以下步骤:

我刚刚将模型python manage.py makemigrations重命名为NewName运行python manage.py makemigrations 。 它会询问你Did you rename the appname.oldName model to NewName? [y/N] Did you rename the appname.oldName model to NewName? [y/N]selectY

运行python manage.py migrate ,它会问你

以下内容types是陈旧的,需要删除:

 appname | oldName appname | NewName 

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

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

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

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

 sudo pip install -U Django 

-U为“升级”),它解决了这个问题。