首页 文章

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

提问于
浏览
114

我打算在现有的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 模型 . 我的理由是我不应该立刻改变太多,如果我将这个字段名称更改为 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

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

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

谢谢!

10 回答

  • 26

    所以,当我尝试这个时,似乎你可以浓缩步骤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甚至更旧的迁移文件(!) .

    Update :正如ceasaro提到的,Django的新版本通常能够检测并询问是否重命名了模型 . 因此,首先尝试 manage.py makemigrations ,然后检查迁移文件 .

  • 7

    起初,我认为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步:

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

    第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 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
    

    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:

    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应该问你是否真的重命名了modelname,说是)

    python manage.py migrate
    

    就是这样!

    这适用于Django1.8

  • 2

    我需要做同样的事情 . 我一下子改变了模型(即步骤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/

  • 96

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

    然后我更改了ForeignKey字段的名称以匹配,Makemigrations再次询问我确认我想要更改名称 . 迁移比做出改变 .

    所以我在没有任何特殊文件编辑的情况下分两步完成 . 我一开始就遇到错误,因为我忘了更改admin.py文件,正如@wasibigeek所提到的那样 .

  • -5

    我正在使用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
    

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

  • 6

    我也面对这个问题,因为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中创建的迁移文件中的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
    

    完成

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

  • 4

    不幸的是,我发现了重命名迁移的问题(每个django 1.x),这会在db中留下旧的表名(甚至不在旧表上尝试任何东西,只需重命名模型) .

    该案例的解决方案:

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

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

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

  • 3

    我会发表@ceasaro的话,我对他的评论answer .

    较新版本的Django可以检测更改并询问已完成的操作 . 我还要补充一点,Django可能会混合一些迁移命令的执行顺序 .

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

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

  • 1

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

    sudo pip install -U Django
    

    -U for "upgrade")它解决了这个问题 .

相关问题