我计划在现有的 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
模型。我的理由是我不应该一次更改太多,如果我将此字段名称更改为 bar
,我可能会丢失该列中的数据。
第2步
创建一个空迁移:
python manage.py makemigrations --empty myapp
第 3 步
编辑在步骤 2 中创建的迁移文件中的 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 步
创建另一个空迁移:
python manage.py makemigrations --empty myapp
第 7 步
编辑在步骤 6 中创建的迁移文件中的 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
除了更新其余代码(视图、表单等)以反映新的变量名称之外,这基本上是新迁移功能的工作方式吗?
此外,这似乎有很多步骤。迁移操作可以以某种方式压缩吗?
谢谢!
因此,当我尝试此操作时,您似乎可以压缩第 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 甚至更旧的迁移文件 (!),您可能会遇到一些错误。
更新:如 ceasaro 所述,较新版本的 Django 通常能够检测并询问模型是否被重命名。所以先试试 manage.py makemigrations
然后检查迁移文件。
起初,我认为 Fiver 的方法对我有用,因为迁移在第 4 步之前运行良好。但是,将“ForeignKeyField(Foo)”隐式更改为“ForeignKeyField(Bar)”与任何迁移无关。这就是我想重命名关系字段时迁移失败的原因(步骤 5-8)。这可能是因为在我的情况下,我的“AnotherModel”和“YetAnotherModel”在其他应用程序中分派。
所以我设法按照以下步骤重命名我的模型和关系字段:
我改编了 this 中的方法,特别是 otranzer 的技巧。
所以就像 Fiver 一样,假设我们在 myapp 中有:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
在 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()
步骤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 模型。 (之前的 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()
然后做:
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
IntegerField
。这对我来说非常有效,并且具有使用正确名称重新创建它们的额外优势。当然,我会建议在实际运行之前检查所有迁移!
ForeignKey
更改为 IntegerField
拯救了我的一天!
在当前版本的 Django 中,您可以重命名模型并运行 python manage.py makemigrations
,然后 django 会询问您是否要重命名模型,如果您选择是,那么所有重命名过程将自动完成。
我需要做同样的事情并遵循。我一次更改了模型(Fiver 的回答中的第 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,然后为应用程序迁移,设法更改了两个模型类名称(包括 ForeignKey 和数据)。对于 Makemigrations 步骤,我必须确认我想要更改表名。 Migrate 更改了表的名称没有问题。
然后我更改了 ForeignKey 字段的名称以匹配,Makemigrations 再次要求我确认我想更改名称。迁移而不是进行更改。
所以我分两步完成了这个,没有任何特殊的文件编辑。正如@wasibigeek 所提到的,我一开始确实遇到了错误,因为我忘记了更改 admin.py 文件。
我也遇到了 v.thorey 描述的问题,发现他的方法非常有用,但可以浓缩为更少的步骤,实际上是第 5 到 8 步,正如 Fiver 描述的那样,没有第 1 到第 4 步,除了第 7 步需要更改为我的在步骤 3 之后。总体步骤如下:
第一步:编辑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 步创建的迁移文件中编辑 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')
]
第 4 步:应用迁移
python manage.py migrate
完毕
PS 我已经在 Django 1.9 上尝试过这种方法
blank=True, null=True
,这种方法对我来说非常有效。一旦我做了我的models.IntegerField(blank=True, null=True)
,这项技术就运作良好。谢谢!
我正在使用 Django 版本 1.9.4
我已遵循以下步骤:-
我刚刚将模型 oldName 重命名为 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
它为我重命名所有现有数据并将其迁移到新的命名表。
只是想确认并添加 ceasaro 评论。 Django 2.0 现在似乎可以自动执行此操作。
我在 Django 2.2.1 上,我所要做的就是重命名模型并运行 makemigrations
。
在这里它询问我是否已将特定类从 A
重命名为 B
,我选择了是并运行了 migrate 并且一切似乎都有效。
请注意,我没有在 project/migrations 文件夹中的任何文件中重命名旧模型名称。
不幸的是,我发现重命名迁移的问题(每个 django 1.x)在数据库中留下了旧的表名。
Django 甚至没有在旧表上尝试任何东西,只是重命名了他自己的模型。外键和一般索引也存在同样的问题 - Django 没有正确跟踪更改。
最简单的解决方案(解决方法):
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
...
Bar = Foo # and use Bar only
真正的解决方案(一种在 2 次提交中切换所有索引、约束、触发器、名称等的简单方法,但适用于较小的表):
提交一个:
创建与旧模型相同的模型
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
...
class Bar(model.Model):
...
切换代码以仅与新模型 Bar 一起使用。 (包括架构上的所有关系)
在迁移中准备 RunPython
,它将数据从 Foo 复制到 Bar(包括 Foo 的 id
)
可选优化(如果需要更大的表)
提交 B:(不急,在迁移整个团队时执行)
旧模型 Foo 的安全丢弃
进一步清理:
壁球移民
Django中的错误:
https://code.djangoproject.com/ticket/23577
我需要重命名几个表。但是 Django 只注意到一个模型重命名。发生这种情况是因为 Django 迭代添加,然后删除模型。对于每一对,它会检查它们是否属于同一应用程序并具有 identical fields。只有一个表没有要重命名的表的外键(外键包含模型类名称,您还记得)。换句话说,只有一张表没有字段变化。这就是它被注意到的原因。
因此,解决方案是一次重命名一个表,更改 models.py
中的模型类名称,可能是 views.py
,然后进行迁移。之后检查您的代码是否有其他引用(模型类名称、相关(查询)名称、变量名称)。如果需要,请进行迁移。然后,可以选择将所有这些迁移合并为一个(确保也复制导入)。
我会根据他对此answer的评论发表@ceasaro 的话。
较新版本的 Django 可以检测更改并询问已完成的操作。我还要补充一点,Django 可能会混合一些迁移命令的执行顺序。
明智的做法是应用小的更改并运行 makemigrations
和 migrate
,如果发生错误,可以编辑迁移文件。
可以更改某些行的执行顺序以避免错误。
migrations.SeparateDatabaseAndState
可以提供帮助吗?
如果您使用的是像 PyCharm 这样好的 IDE,您可以右键单击模型名称并进行重构 -> 重命名。这样可以省去遍历所有引用模型的代码的麻烦。然后运行 makemigrations 并迁移。 Django 2+ 将简单地确认名称更改。
我将 Django 从版本 10 升级到版本 11:
sudo pip install -U Django
(-U
表示“升级”),它解决了问题。
apps.get_model
,则不必更改模型名称。我花了很多时间才弄清楚这一点。./manage.py makemigrations myapp
命令将询问您是否重命名了模型。例如:您是否将 myapp.Foo 模型重命名为 Bar? [y/N] 如果您回答 'y',您的迁移将包含migration.RenameModel('Foo', 'Bar')
重命名字段的相同计数 :-)manage.py makemigrations myapp
可能仍会失败:“如果您同时更改模型的名称及其相当多的字段,您可能必须手动添加它;对于自动检测器,这看起来就像您删除了具有旧名称的模型并添加了一个具有不同名称的新表,它创建的迁移将丢失旧表中的所有数据。” Django 2.1 Docs 对我来说,创建一个空迁移,向其中添加模型重命名,然后像往常一样运行makemigrations
就足够了。makemigrations
,然后更改模型,然后再次makemigrations
。