Python 用于重命名模型和关系字段的 Django 迁移策略
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25091130/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Django migration strategy for renaming a model and relationship fields
提问by Fiver
I'm planning to rename several models in an existing Django project where there are many other models that have foreign key relationships to the models I would like to rename. I'm fairly certain this will require multiple migrations, but I'm not sure of the exact procedure.
我计划重命名现有 Django 项目中的多个模型,其中还有许多其他模型与我想重命名的模型具有外键关系。我相当肯定这将需要多次迁移,但我不确定确切的过程。
Let's say I start out with the following models within a Django app called myapp:
假设我从一个名为 的 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()
I want to rename the Foomodel because the name doesn't really make sense and is causing confusion in the code, and Barwould make for a much clearer name.
我想重命名Foo模型,因为该名称实际上没有意义并且会导致代码混淆,并且Bar会使名称更加清晰。
From what I have read in the Django development documentation, I'm assuming the following migration strategy:
根据我在 Django 开发文档中阅读的内容,我假设以下迁移策略:
Step 1
第1步
Modify models.py:
修改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()
Note the AnotherModelfield name for foodoesn't change, but the relation is updated to the Barmodel. My reasoning is that I shouldn't change too much at once and that if I changed this field name to barI would risk losing the data in that column.
请注意,AnotherModel字段名称foo不变,但关系已更新到Bar模型。我的理由是我不应该一次更改太多,如果我将此字段名称更改为bar我可能会丢失该列中的数据。
Step 2
第2步
Create an empty migration:
创建一个空的迁移:
python manage.py makemigrations --empty myapp
Step 3
第 3 步
Edit the Migrationclass in the migration file created in step 2 to add the RenameModeloperation to the operations list:
编辑Migration在步骤 2 中创建的迁移文件中的类,将RenameModel操作添加到操作列表中:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameModel('Foo', 'Bar')
]
Step 4
第四步
Apply the migration:
应用迁移:
python manage.py migrate
Step 5
第 5 步
Edit the related field names in models.py:
编辑中的相关字段名称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()
Step 6
第 6 步
Create another empty migration:
创建另一个空迁移:
python manage.py makemigrations --empty myapp
Step 7
第 7 步
Edit the Migrationclass in the migration file created in step 6 to add the RenameFieldoperation(s) for any related field names to the operations list:
编辑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')
]
Step 8
第 8 步
Apply the 2nd migration:
应用第二次迁移:
python manage.py migrate
Aside from updating the rest of the code (views, forms, etc.) to reflect the new variable names, is this basically how the new migration functionality would work?
除了更新其余代码(视图、表单等)以反映新的变量名称之外,这基本上是新迁移功能的工作方式吗?
Also, this seems like a lot of steps. Can the migration operations be condensed in some way?
此外,这似乎有很多步骤。能否以某种方式压缩迁移操作?
Thanks!
谢谢!
采纳答案by wasabigeek
So when I tried this, it seems you can condense Step 3 - 7:
因此,当我尝试此操作时,您似乎可以压缩步骤 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')
]
You may get some errors if you don't update the names where it's imported e.g. admin.py and even older migration files (!).
如果您不更新导入的名称,例如 admin.py 甚至更旧的迁移文件(!),您可能会遇到一些错误。
Update: As ceasaromentions, newer versions of Django are usually able to detect and ask if a model is renamed. So try manage.py makemigrationsfirst and then check the migration file.
更新:正如ceasaro提到的,较新版本的Django 通常能够检测并询问模型是否已重命名。所以manage.py makemigrations先尝试,然后检查迁移文件。
回答by John Q
I needed to do the same thing and follow. I changed the model all at once (Steps 1 and 5 together from Fiver's answer). Then created a schema migration but edited it to be this:
我需要做同样的事情并遵循。我一次更改了模型(来自 Fiver 的回答的第 1 步和第 5 步)。然后创建了一个架构迁移,但将其编辑为:
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_table('Foo','Bar')
def backwards(self, orm):
db.rename_table('Bar','Foo')
This worked perfectly. All my existing data showed up, all the other tables referenced Bar fine.
这工作得很好。我所有现有的数据都显示出来了,所有其他表都很好地引用了 Bar。
from here: https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/
从这里:https: //hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/
回答by v.thorey
At first, I thought that Fiver's method worked for me because the migration worked well until step 4. However, the implicit changes 'ForeignKeyField(Foo)' into 'ForeignKeyField(Bar)' was not related in any migrations. This is why migration failed when I wanted to rename relationship fields (step 5-8). This might be due to the fact that my 'AnotherModel' and 'YetAnotherModel' are dispatched in other apps in my case.
起初,我认为 Fiver 的方法对我有用,因为在第 4 步之前迁移工作得很好。但是,将 'ForeignKeyField(Foo)' 隐式更改为 'ForeignKeyField(Bar)' 在任何迁移中都没有关系。这就是当我想重命名关系字段时迁移失败的原因(步骤 5-8)。这可能是因为在我的情况下,我的“AnotherModel”和“YetAnotherModel”是在其他应用程序中分派的。
So I managed to rename my models and relationship fields doing following below steps:
所以我设法按照以下步骤重命名我的模型和关系字段:
I adapted the method from thisand particularly the trick of otranzer.
我改编自方法本和otranzer的特别的特技。
So like Fiver let's say we have in myapp:
就像 Fiver 一样,假设我们在myapp 中有:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
And in myotherapp:
在myotherapp 中:
class AnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_ridonkulous = models.BooleanField()
Step 1:
第1步:
Transform every OneToOneField(Foo) or ForeignKeyField(Foo) into IntegerField(). (This will keep the id of related Foo object as value of the integerfield).
将每个 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()
Then
然后
python manage.py makemigrations
python manage.py migrate
Step 2: (Like step 2-4 from Fiver)
第 2 步:(如 Fiver 的第 2-4 步)
Change the model name
更改型号名称
class Bar(models.Model): # <-- changed model name
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
Create an empty migration:
创建一个空的迁移:
python manage.py makemigrations --empty myapp
Then edit it like:
然后像这样编辑它:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameModel('Foo', 'Bar')
]
Eventually
最终
python manage.py migrate
Step 3:
第 3 步:
Transform Back your IntegerField() into their previous ForeignKeyField or OneToOneField but with the new Bar Model. (The previous integerfield was storing the id, so django understand that and reestablish the connection, which is cool.)
将您的 IntegerField() 转换回之前的 ForeignKeyField 或 OneToOneField,但使用新的 Bar 模型。(之前的 integerfield 是存储 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()
Then do:
然后做:
python manage.py makemigrations
Very importantly, at this step you have to modify every new migrations and add the dependency on the RenameModel Foo-> Bar migrations. So if both AnotherModel and YetAnotherModel are in myotherapp the created migration in myotherapp must look like this:
非常重要的是,在此步骤中,您必须修改每个新迁移并添加对 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')
),
]
Then
然后
python manage.py migrate
Step 4:
第四步:
Eventually you can rename your fields
最终你可以重命名你的字段
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()
and then do automatic renaming
然后进行自动重命名
python manage.py makemigrations
(django should ask you if you actually renamed the modelname, say yes)
(django 应该问你是否真的重命名了模型名称,说是)
python manage.py migrate
And that's it!
就是这样!
This works on Django1.8
这适用于 Django1.8
回答by S?awomir Lenart
Unfortunately, I found problems (each django 1.x) with rename migration which leave old table names in the database.
不幸的是,我发现重命名迁移的问题(每个 django 1.x)在数据库中留下旧表名。
Django doesn't even try anything on the old table, just rename his own model. The same problem with foreign keys, and indices in general - changes there are not tracked properly by Django.
Django 甚至没有在旧表上尝试任何东西,只是重命名自己的模型。外键和一般索引的相同问题 - Django 没有正确跟踪那里的更改。
The simplest solution (workaround):
最简单的解决方案(解决方法):
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
...
Bar = Foo # and use Bar only
The real solution (an easy way to switch all indices, constraints, triggers, names, etc in 2 commits, but rather for smallertables):
真正的解决方案(一种在 2 次提交中切换所有索引、约束、触发器、名称等的简单方法,但适用于较小的表):
commit A:
提交A:
- create the samemodel as the old one
- 创建与旧模型相同的模型
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
...
class Bar(model.Model):
...
- switch code to work with new model
Baronly. (including all relations on the schema)
- 切换代码以
Bar仅使用新模型。(包括架构上的所有关系)
In migration prepare RunPython, which copy data from Foo to Bar
(including idof Foo)
在迁移准备中RunPython,将数据从 Foo 复制到 Bar(包括idFoo)
- optional optimization (if needed for greater tables)
- 可选优化(如果需要更大的表)
commit B:(no rush, do it when an entire team is migrated)
commit B:(不急,等整个团队都迁移了再做)
- safe drop of the old model
Foo
- 旧模型的安全掉落
Foo
further cleanup:
进一步清理:
- squash on migrations
- 壁球迁移
bug in Django:
Django 中的错误:
回答by Curtis Lo
I also faced the problem as v.thorey described and found that his approach is very useful but can be condensed into fewer steps which are actually step 5 to 8 as Fiver described without step 1 to 4 except that step 7 needs to be changed as my below step 3. The overall steps are as follow:
我也遇到了 v.thorey 描述的问题,发现他的方法非常有用,但可以浓缩成更少的步骤,实际上是步骤 5 到 8,如 Fiver 所述,没有步骤 1 到 4,只是步骤 7 需要更改为我的下面是第3步。 总体步骤如下:
Step 1: Edit the related field names in models.py
第一步:编辑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()
Step 2: Create an empty migration
第 2 步:创建空迁移
python manage.py makemigrations --empty myapp
Step 3: Edit the Migration class in the migration file created in Step 2
第 3 步:编辑第 2 步中创建的迁移文件中的 Migration 类
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')
]
Step 4: Apply the migration
步骤 4:应用迁移
python manage.py migrate
Done
完毕
P.S. I've tried this approach on Django 1.9
PS 我已经在 Django 1.9 上尝试过这种方法
回答by Piyush S. Wanare
I am using Django version 1.9.4
我正在使用 Django 版本 1.9.4
I have follow the following steps:-
我已按照以下步骤操作:-
I have just rename the model oldName to NewName
Run python manage.py makemigrations. It will ask you for
Did you rename the appname.oldName model to NewName? [y/N]select Y
我刚刚将模型 oldName 重命名为 NewName Run python manage.py makemigrations。它会要求您
Did you rename the appname.oldName model to NewName? [y/N]选择 Y
Run python manage.py migrateand it will ask you for
运行python manage.py migrate它会问你
The following content types are stale and need to be deleted:
以下内容类型已过时,需要删除:
appname | oldName
appname | NewName
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'.
通过外键与这些内容类型相关的任何对象也将被删除。确定要删除这些内容类型吗?如果您不确定,请回答“否”。
Type 'yes' to continue, or 'no' to cancel: Select No
It rename and migrate all existing data to new named table for me.
它为我重命名所有现有数据并将其迁移到新命名的表。
回答by excyberlabber
For Django 1.10, I managed to change two model class names (including a ForeignKey, and with data) by simply running Makemigrations, and then Migrate for the app. For the Makemigrations step, I had to confirm that I wanted to change the table names. Migrate changed the names of the tables without a problem.
对于 Django 1.10,我通过简单地运行 Makemigrations 和 Migrate for the app 设法更改了两个模型类名称(包括外键和数据)。对于 Makemigrations 步骤,我必须确认我想要更改表名。Migrate 毫无问题地更改了表的名称。
Then I changed the name of the ForeignKey field to match, and again was asked by Makemigrations to confirm that I wanted to change the name. Migrate than made the change.
然后我将 ForeignKey 字段的名称更改为匹配,再次被 Makemigrations 要求确认我想更改名称。迁移而不是进行更改。
So I took this in two steps without any special file editing. I did get errors at first because I forgot to change the admin.py file, as mentioned by @wasibigeek.
所以我分两步完成,没有任何特殊的文件编辑。正如@wasibigeek 所提到的,我一开始确实遇到了错误,因为我忘记更改 admin.py 文件。
回答by Muhammad Hafid
I upgraded Django from version 10 to version 11:
我将 Django 从版本 10 升级到版本 11:
sudo pip install -U Django
(-Ufor "upgrade") and it solved the problem.
(-U用于“升级”)并解决了问题。
回答by x-yuri
I needed to rename a couple of tables. But only one model rename was noticed by Django. That happened because Django iterates over added, then removed models. For each pair it checks if they're of the same app and have identical fields. Only one table had no foreign keys to tables to be renamed (foreign keys contain model class name, as you remember). In other words, only one table had no field changes. That's why it was noticed.
我需要重命名几个表。但是 Django 只注意到了一个模型重命名。发生这种情况是因为 Django 迭代添加,然后删除模型。对于每一对,它检查它们是否属于同一个应用程序并具有相同的字段。只有一张表没有要重命名的表的外键(外键包含模型类名称,如您所记得的)。换句话说,只有一张表没有字段更改。这就是它被注意到的原因。
So, the solution is to rename one table at a time, changing model class name in models.py, possibly views.py, and making a migration. After that inspect your code for other references (model class names, related (query) names, variable names). Make a migration, if needed. Then, optionally combine all these migrations into one (make sure to copy imports as well).
因此,解决方案是一次重命名一个表,更改 中的模型类名models.py,可能views.py,并进行迁移。之后检查您的代码是否有其他引用(模型类名称、相关(查询)名称、变量名称)。如果需要,进行迁移。然后,可选择将所有这些迁移合并为一个(确保也复制导入)。
回答by diogosimao
I would make @ceasaro words, mine on his comment on this answer.
我会让@ceasaro 的话,我对他对这个答案的评论。
Newer versions of Django can detect changes and ask about what was done. I also would add that Django might mix the order of execution of some migration commands.
较新版本的 Django 可以检测更改并询问已完成的操作。我还要补充一点,Django 可能会混合某些迁移命令的执行顺序。
It would be wise to apply small changes and run makemigrationsand migrateand if the error occurs the migration file can be edited.
这将是明智的,适用小的变化和运行makemigrations以及migrate如果发生错误的迁移文件进行编辑。
Some lines order of execution can be changed to avoid erros.
可以更改某些行的执行顺序以避免错误。

