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()
请注意, foo
的AnotherModel
字段名称不会更改,但关系会更新为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
为“升级”),它解决了这个问题。