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

Fiv*_*ver 133 python django django-migrations

我打算在现有的Django项目中重命名几个模型,其中有许多其他模型与我想要重命名的模型有外键关系.我很确定这需要多次迁移,但我不确定具体的程序.

假设我开始在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()
Run Code Online (Sandbox Code Playgroud)

我想重命名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()
Run Code Online (Sandbox Code Playgroud)

请注意,AnotherModel字段名称foo不会更改,但关系会更新为Bar模型.我的理由是我不应该立刻改变太多,如果我改变这个字段名称,bar我将冒失去该列中的数据的风​​险.

第2步

创建一个空迁移:

python manage.py makemigrations --empty myapp
Run Code Online (Sandbox Code Playgroud)

第3步

编辑Migration在步骤2中创建的迁移文件中的类,将RenameModel操作添加到操作列表:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]
Run Code Online (Sandbox Code Playgroud)

第4步

应用迁移:

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

第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()
Run Code Online (Sandbox Code Playgroud)

第6步

创建另一个空迁移:

python manage.py makemigrations --empty myapp
Run Code Online (Sandbox Code Playgroud)

第7步

编辑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')
    ]
Run Code Online (Sandbox Code Playgroud)

第8步

应用第二次迁移:

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢!

was*_*eek 111

所以,当我尝试这个时,似乎你可以浓缩步骤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')
    ]
Run Code Online (Sandbox Code Playgroud)

如果不更新导入的名称,例如admin.py甚至更旧的迁移文件(!),则可能会出现一些错误.

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

  • 在django 2.0中如果更改了模型名称,`./ manage.py makemigrations myapp`命令会询问您是否重命名了模型.例如:**您是否将myapp.Foo模型重命名为Bar?[y/N]**如果你回答''y'**你的迁移将包含`migration.RenameModel('Foo','Bar')`重命名字段的相同数量:-) (7认同)
  • 如果在其中使用"apps.get_model",则无需更改迁移文件中的模型名称.我花了很多时间来弄清楚这一点. (4认同)
  • `manage.py makemigrations myapp` 可能仍然会失败:“如果您一次更改模型的名称及其相当多的字段,您可能必须手动添加此内容;对于自动检测器来说,这看起来就像您删除了旧模型名称并添加了一个具有不同名称的新表,它创建的迁移将丢失旧表中的所有数据。” [Django 2.1 Docs](https://docs.djangoproject.com/en/2.1/ref/migration-operations/#renamemodel) 对我来说,创建一个空迁移就足够了,向其中添加模型重命名,然后运行像往常一样`makemigrations`。 (3认同)
  • 使用现有数据对其进行了尝试,尽管在我的本地环境中的sqlite上只有几行(当我转到生产环境时,我打算清除所有内容,包括迁移文件) (2认同)

Jay*_*nan 33

在当前版本的 Django 中,您可以重命名模型并运行python manage.py makemigrations,django 将询问您是否要重命名模型,如果您选择“是”,则所有重命名过程将自动完成。

  • 这确实需要成为当前的首要答案,尽管旧的答案仍然有用且有趣。如今 Django 就是为你做的。 (7认同)

v.t*_*rey 31

起初,我认为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)
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

步骤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()
Run Code Online (Sandbox Code Playgroud)

然后

python manage.py makemigrations

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

第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)
Run Code Online (Sandbox Code Playgroud)

创建一个空迁移:

python manage.py makemigrations --empty myapp
Run Code Online (Sandbox Code Playgroud)

然后编辑它:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]
Run Code Online (Sandbox Code Playgroud)

终于

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

第3步:

将IntegerField()转换回之前的ForeignKeyField,OneToOneField,但使用新的Bar Model.(之前的整数字段存储了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()
Run Code Online (Sandbox Code Playgroud)

然后做:

python manage.py makemigrations 
Run Code Online (Sandbox Code Playgroud)

非常重要的是,在此步骤中,您必须修改每个新迁移并添加对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')
        ),
    ]
Run Code Online (Sandbox Code Playgroud)

然后

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

第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()
Run Code Online (Sandbox Code Playgroud)

然后进行自动重命名

python manage.py makemigrations
Run Code Online (Sandbox Code Playgroud)

(django应该问你是否真的重命名了modelname,说是)

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

就是这样!

这适用于Django1.8

  • 谢谢!这非常有帮助.但是注意 - 我还必须手动重命名和/或删除PostgreSQL字段索引,因为在将Foo重命名为Bar之后,我创建了一个名为Bar的新模型. (3认同)

Joh*_*n Q 7

我需要做同样的事情.我一下子改变了模型(即步骤1和步骤5一起).然后创建了一个模式迁移,但将其编辑为:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')
Run Code Online (Sandbox Code Playgroud)

这非常有效.我现有的所有数据都显示出来了,所有其他表都引用了Bar.

从这里:https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/


小智 6

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

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

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


Peh*_*eje 6

只是想确认并补充塞萨罗的评论。Django 2.0 现在似乎自动执行此操作。

我使用的是 Django 2.2.1,我所要做的就是重命名模型并运行makemigrations.

这里它询问我是否已将特定类从Ato重命名为B,我选择了 yes 并运行 migrate ,一切似乎都有效。

注意我没有重命名项目/迁移文件夹内的任何文件中的旧模型名称。


小智 5

我也遇到了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()
Run Code Online (Sandbox Code Playgroud)

步骤2:建立一个空的迁移

python manage.py makemigrations --empty myapp
Run Code Online (Sandbox Code Playgroud)

步骤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')
]
Run Code Online (Sandbox Code Playgroud)

步骤4:套用迁移

python manage.py migrate
Run Code Online (Sandbox Code Playgroud)

完成了

PS我已经在Django 1.9上尝试过这种方法


Piy*_*are 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
Run Code Online (Sandbox Code Playgroud)

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

Type 'yes' to continue, or 'no' to cancel: Select No
Run Code Online (Sandbox Code Playgroud)

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