迁移django-model字段名称更改而不会丢失数据

sfl*_*che 34 django-models customcolumn django-south custom-fields

我有一个django项目,其中包含一个已包含数据的数据库表.我想更改字段名称而不会丢失该列中的任何数据.我最初的计划是简单地以一种实际上不会改变db表名称的方式更改模型字段名称(使用db_columncolumn参数):

原型号:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

新型号:

class Foo(models.Model):
  name = models.CharField(max_length=50, db_column='orig_name')
Run Code Online (Sandbox Code Playgroud)

但是,运行South's schemamigration --auto会生成一个删除原始列的迁移脚本orig_name,并添加一个新列name,这会产生删除该列中数据的不良副作用.(我也很困惑为什么South想要更改db中列的名称,因为我对db_column的理解是它允许更改模型字段名而不更改数据库表列的名称).

如果我无法在不更改db字段的情况下更改模型字段,我想我可以做更直接的名称更改,如下所示:

原型号:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

新型号:

class Foo(models.Model):
  name = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

无论我最终使用哪种策略(我更喜欢第一种,但会发现第二种可接受),我主要担心的是确保我不会丢失该列中已有的数据.

这需要一个多步骤的过程吗?(例如1.添加列,2.将数据从旧列迁移到新列,以及3.删除原始列)或者我可以使用类似的方式更改迁移脚本db.alter_column吗?

更改列名称时保留该列中数据的最佳方法是什么?

Ami*_*ber 34

在保留DB字段的同时更改字段名称

为Django 1.8+添加答案(使用Django本地迁移,而不是南部).

进行首先添加db_column属性的迁移,然后重命名该字段.Django理解第一个是无操作(因为它改变了db_column保持不变),第二个是无操作(因为它没有进行模式更改).我实际上检查了日志,看看没有架构更改......

operations = [
    migrations.AlterField(
        model_name='mymodel',
        name='oldname',
        field=models.BooleanField(default=False, db_column=b'oldname'),
    ),
    migrations.RenameField(
        model_name='mymodel',
        old_name='oldname',
        new_name='newname',
    ),
]
Run Code Online (Sandbox Code Playgroud)

  • 当心!这些操作在某些情况下会触发更改,例如外键(“DROP/SET CONSTRAINT”)。我通过艰难的方式学到了它(在生产中)。我向 Django 提交了一张票证并执行了拉取请求来修复这些情况。https://code.djangoproject.com/ticket/31825 https://code.djangoproject.com/ticket/31826 也就是说,您可以使用`manage.py sqlmigrate <app_name> <migration>`来检查哪些SQL命令它会运行。它依赖于数据库,因此请使用与生产中相同的数据库类型运行它。 (3认同)
  • @ luizs81这取决于您是否要在数据库中重命名列。没有AlterField,您将进行模式更改 (2认同)

Wol*_*lph 17

这很容易修复.但您必须自己修改迁移.

而不是删除和添加列,使用db.rename_column.您只需修改创建的迁移即可schemamigration --auto


Pau*_*ole 6

实际上,在Django 1.10中,只需重命名模型中的字段,然后运行makemigrations,即可立即识别操作(即,一个字段消失了,而另一个字段代替了它):

$ ./manage.py makemigrations
Did you rename articlerequest.update_at to articlerequest.updated_at (a DateTimeField)? [y/N] y
Migrations for 'article_requests':
  article_requests/migrations/0003_auto_20160906_1623.py:
    - Rename field update_at on articlerequest to updated_at
Run Code Online (Sandbox Code Playgroud)

  • 只是想强调如果您确实想重命名,Django 实际上会询问(我想您可以回答“否”并取消您正在运行的 makemigrations) (2认同)

Aar*_*rif 6

更新

使用Django 2.0.9对它进行了测试,它可以自动检测某个字段是否被重命名,并提供重命名选项,而不是删除并创建新字段 在此处输入图片说明

初始

发布(如果仍然对某人有用)。

对于Django 2.0 +,只需在模型中重命名字段

class Foo(models.Model):
    orig_name = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

class Foo(models.Model):
    name = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

现在运行python manage.py makemigrations 它会生成带有删除旧字段并添加新字段的操作的迁移。

继续并将其更改为跟随。

operations = [
    migrations.RenameField(
        model_name='foo',
        old_name='orig_name',
        new_name='name')
]
Run Code Online (Sandbox Code Playgroud)

现在运行python manage.py migrate它将在数据库中重命名该列而不会丢失数据。

  • 只是一个附录,如果你想这样做,请确保你没有同时更改字段中的*任何内容*,否则 Django 不会将其检测为名称更改,而是检测为字段删除和添加新字段。我尝试删除 `blank=True, null=True` 并设置相同的默认值,但它不起作用。一旦我恢复了它并只更改了名称,它就起作用了! (3认同)
  • 我感谢你的回答@Aarif,这对我很有帮助。如果您要重命名一两个字段,那么使用此方法应该没问题。但是,如果您一次重命名许多字段(无论如何都不建议这样做),您可能会遇到问题。 (2认同)

Lou*_*uis 5

我遇到了这种情况。我想更改模型中的字段名称,但保持列名称相同。

我做的方法就是这样做schemamigration --empty [app] [some good name for the migration]。问题是,就South而言,更改模型中的字段名称需要处理的更改。因此,必须创建一个迁移。但是,我们知道在数据库方面无需执行任何操作。因此,空迁移避免了对数据库进行不必要的操作,但满足了South处理其认为是变更的需求。

请注意,如果您使用loaddata或使用Django的测试夹具工具(loaddata在后台使用)。您必须更新固定装置以使用新的字段名称,因为固定装置基于模型字段名称,而不是数据库字段名称。

对于数据库中列名确实发生更改的情况,我不建议将其db.rename_column用于列迁移。我在此答案中使用sjh描述的方法:

我已经将新列添加为一个schemamigration,然后创建了一个datamigration将值移到新字段中,然后创建了另一个schemamigration以删除旧列。

正如我在对该问题的评论中所指出的那样,问题db.rename_column在于它没有将约束与列一起重命名。这个问题是仅仅是表面上的,还是意味着将来的迁移可能会因为找不到约束而失败,这对我来说还是未知的。