70

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

原型号:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

新型号:

class Foo(models.Model):
  name = models.CharField(max_length=50, db_column='orig_name')

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

如果我无法在不更改 db 字段的情况下更改模型字段,我想我可以像这样进行更直接的名称更改:

原型号:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

新型号:

class Foo(models.Model):
  name = models.CharField(max_length=50)

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

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

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

4

10 回答 10

58

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

添加 Django 1.8+ 的答案(使用 Django-native 迁移,而不是 South)。

进行首先添加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',
    ),
]
于 2015-10-17T21:10:56.343 回答
46

Django 2.0.9(及更高版本)可以自动检测字段是否被重命名,并提供重命名而不是删除和创建新字段的选项

(同样适用于Django 2.2在此处输入图像描述

初步答案

发布,如果它仍然对某人有帮助。

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

class Foo(models.Model):
    orig_name = models.CharField(max_length=50)

class Foo(models.Model):
    name = models.CharField(max_length=50)

现在运行python manage.py makemigrations 它将生成迁移,其中包含删除旧字段和添加新字段的操作。

继续并将其更改为以下。

operations = [
    migrations.RenameField(
        model_name='foo',
        old_name='orig_name',
        new_name='name')
]

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

于 2019-01-31T12:01:30.233 回答
18

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

而不是删除和添加列,使用db.rename_column. 您可以简单地修改由schemamigration --auto

于 2010-08-16T23:14:07.530 回答
10

实际上在 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
于 2016-09-06T16:26:51.240 回答
5

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

我做的方式就是做schemamigration --empty [app] [some good name for the migration]。问题在于,就 South 而言,更改模型中的字段名称是它需要处理的更改。因此必须创建迁移。但是,我们知道在数据库端不需要做任何事情。因此,空迁移避免了对数据库进行不必要的操作,同时满足了 South 处理它认为是更改的需要。

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

对于数据库中列名确实发生变化的情况,我从不推荐使用db.rename_column列迁移。我在这个答案中使用了 sjh 描述的方法:

我已将新列添加为一个模式迁移,然后创建了一个数据迁移以将值移动到新字段中,然后创建第二个模式迁移以删除旧列

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

于 2015-01-29T13:07:24.987 回答
4

可以在不进行任何手动迁移文件编辑的情况下重命名字段:

▶︎ 从这样的事情开始:

class Foo(models.Model):
  old_name = models.CharField(max_length=50)

▶︎ 将 db_column= OLD_FIELD_NAME添加到原始字段。

class Foo(models.Model):
  old_name = models.CharField(max_length=50, db_column='old_name')

▶︎ 运行:python3 manage.py makemigrations

▶︎ 将字段从OLD_FIELD_NAME重命名为NEW_FIELD_NAME

class Foo(models.Model):
  new_name = models.CharField(max_length=50, db_column='old_name')

▶︎ 运行:python3 manage.py makemigrations

系统会提示您:

您是否重命名了MODELOLD_FIELD_NAMEMODELNEW_FIELD_NAME(外键)?[是/否] 是

这将生成两个迁移文件,而不仅仅是一个,尽管这两个迁移都是自动生成的。

此过程适用于 Django 1.7+。

于 2015-12-22T23:07:21.663 回答
2

更新Django 3.1中,一次只更改一个字段非常简单。

就我而言:

旧字段名称为:is_admin 新字段名称为:is_superuser

当我通过它进行迁移时,python manage.py makemigrations它问我是否要重命名该字段。我只是点击y重命名。然后我迁移python manage.py migrate. 在我的案例中,终端历史如下所示: 在此处输入图像描述

注意:我一次没有测试多个字段。

于 2020-11-11T05:54:14.707 回答
2

我在 Django 1.7.7 上遇到了这种情况。我最终做了以下对我有用的事情。

./manage.py makemigrations <app_name> --empty

migrations.RenameField添加了一个不涉及数据库的简单子类:

class RenameFieldKeepDatabaseColumn(migrations.RenameField):
def database_backwards(self, app_label, schema_editor, from_state, to_state):
    pass

def database_forwards(self, app_label, schema_editor, from_state, to_state):
    pass
于 2015-11-10T21:16:18.970 回答
0

正如其他回复中所指出的,现在很容易重命名一个字段,而不会使用db_column. 但是生成的迁移实际上会创建一些 SQL 语句。./manage.py sqlmigrate ...您可以通过调用迁移来验证这一点。

为了避免对您的数据库产生任何影响,您需要SeparateDatabaseAndState向 Django 表明它不需要在 DB 中执行某些操作。

如果你想了解更多,我写了一篇关于它的小文章。

于 2021-03-10T10:37:11.017 回答
-1

1.编辑django模型上的字段名

2.创建一个空迁移,如下所示:

$ python manage.py makemigrations --empty testApp (testApp 是你的应用程序名称)

  1. 编辑最近创建的空迁移文件

    操作 = [ migrations.RenameField('你的模型', '旧字段', '新字段'), ]

  2. 应用迁移
    $ python manage.py migrate

数据库列名将更改为新名称。

于 2021-05-20T07:44:20.377 回答