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

发布于 2024-09-14 18:51:05 字数 1188 浏览 3 评论 0原文

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

原始模型:

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 的 schemamigration --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 之类的内容更改迁移脚本吗?

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

I have a django project with a database table that already contains data. I'd like to change the field name without losing any of the data in that column. My original plan was to simply change the model field name in a way that would not actually alter the name of the db table (using the db_column column parameter):

The original model:

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

The new model:

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

But, running South's schemamigration --auto produces a migration script that deletes the original column, orig_name, and adds a new column, name, which would have the unwanted side effect of deleting the data in that column. (I'm also confused as to why South wants to change the name of the column in the db, since my understanding of db_column was that it enables a change to the model field name without changing the name of the database table column).

If I can't get away with changing the model field without changing the db field, I guess I could do a more straight forward name change like so:

The original model:

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

The new model:

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

Regardless of which strategy I end up using (I would prefer the first, but would find the second acceptable), my primary concern is ensuring that I don't lose the data that is already in that column.

Does this require a multi-step process? (such as 1. adding a column, 2. migrating the data from the old column to the new column, and 3. removing the original column)
Or can I alter the migration script with something like db.alter_column?

What is the best way to preserve the data in that column while changing the column's name?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

夜雨飘雪 2024-09-21 18:51:05

更改字段名称,同时保留 DB 字段

添加 Django 1.8+ 的答案(使用 Django 原生迁移,而不是 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',
    ),
]

Changing the field name while keeping the DB field

Adding an answer for Django 1.8+ (with Django-native migrations, rather than South).

Make a migration that first adds a db_column property, and then renames the field. Django understands that the first is a no-op (because it changes the db_column to stay the same), and that the second is a no-op (because it makes no schema changes). I actually examined the log to see that there were no schema changes...

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',
    ),
]
戈亓 2024-09-21 18:51:05

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 它将重命名数据库中的列而不会丢失数据。

Django 2.0.9 (and onwards) can automatically detect if a field was renamed and gives an option to rename instead of delete and create a new one

(same works for Django 2.2)
enter image description here

Initial answer

Posting, if it's still helpful for someone.

For Django 2.0 + simply rename the field in the model

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

to

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

Now run python manage.py makemigrations
It'll generate migration with operations for removing the old field and adding the new one.

Go ahead and change that to following.

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

Now run python manage.py migrate it'll rename the column in DB without losing data.

挽袖吟 2024-09-21 18:51:05

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

不要删除并添加列,而是使用 db .rename_column。您可以简单地修改由 schemamigration --auto 创建的迁移

It is quite easy to fix. But you will have to modify the migration yourself.

Instead of dropping and adding the column, use db.rename_column. You can simply modify the migration created by schemamigration --auto

贪了杯 2024-09-21 18:51:05

实际上,在 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

Actually with Django 1.10, just renaming the field in the model and then running makemigrations, immediately identifies the operation (ie. one field disappeared, another appeared in its stead):

$ ./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
千秋岁 2024-09-21 18:51:05

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

我的方法是执行 schemamigration --empty [app] [迁移的一些好名字]。问题是,就 South 而言,更改模型中的字段名称是它需要处理的更改。因此必须创建迁移。然而,我们知道数据库方面不需要做任何事情。 因此,空迁移可以避免对数据库进行不必要的操作,同时满足 South 处理其认为的更改的需求。

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

对于数据库中列名称发生更改的情况,我从不建议使用 db.rename_column 进行列迁移。我使用 sjh 在这个答案中描述的方法:

我已将新列添加为一个架构迁移,然后创建一个数据迁移以将值移至新字段,然后创建第二个架构迁移以删除旧列

正如我在 评论这个问题,db.rename_column的问题是它不会与列一起重命名约束。我不知道这个问题是否仅仅是表面现象,或者这是否意味着未来的迁移可能会因为找不到约束而失败。

I've run into this situation. I wanted to change the field names in the model but keep the column names the same.

The way I've done it is to do schemamigration --empty [app] [some good name for the migration]. The problem is that as far as South is concerned, changing the field names in the model is a change that it needs to handle. So a migration has to be created. However, we know there is nothing that has to be done on the database side. So an empty migration avoids doing unnecessary operation on the database and yet satisfies South's need to handle what it considers to be a change.

Note that if you use loaddata or use Django's test fixture facility (which uses loaddata behind the scenes). You'll have to update the fixtures to use the new field name because the fixtures are based on the model field names, not the database field names.

For cases where column names do change in the database, I never recommend the use db.rename_column for column migrations. I use the method described by sjh in this answer:

I have added the new column as one schemamigration, then created a datamigration to move values into the new field, then a second schemamigration to remove the old column

As I've noted in a comment on that question, the problem with db.rename_column is that it does not rename constraints together with the column. Whether the issue is merely cosmetic or whether this mean a future migration may fail because it cannot find a constraint is unknown to me.

木有鱼丸 2024-09-21 18:51:05

这是针对 Django 4.0 的。
让我们通过一个例子来实现这一点。

我原来的字段名称是 anticipated_end_date,我需要将其命名为 tentative_end_date。按照以下步骤完成操作,

  1. 在模型
  2. 运行python manage.py makemigrations内将anticipated_end_date更改为tentative_end_date。理想情况下,它会显示以下消息

Is your_model_name.anticipated_end_date 重命名为 your_model_name.tentative_end_date (DateField)吗? [y/N]

如果显示此消息,则只需按 y 即可进行迁移,因为它将生成正确的迁移。但是,如果 makemigrations 命令不询问重命名模型字段,则进入生成的迁移并按以下方式更改操作内容:

    operations = [
        migrations.RenameField(
            model_name='your_model_name',
            old_name='anticipated_end_date',
            new_name='tentative_end_date',
        ),
    ]

现在您可以运行python 管理.py 迁移。

这样您的模型字段/数据库列将被重命名,并且您的数据不会丢失。

This is for Django 4.0.
Let's do this with an example.

My original field name was anticipated_end_date, I need to name it tentative_end_date. Follow the following steps to complete the operation

  1. Change the anticipated_end_date to tentative_end_date inside the model
  2. run python manage.py makemigrations. Ideally, it would show the following message

Was your_model_name.anticipated_end_date renamed to your_model_name.tentative_end_date (a DateField)? [y/N]

If it shows this message, then just press y and you are good to migrate, as it will generate the correct migration. However, if makemigrations command does not ask about renaming the model field, then go inside the generated migration and change the operations content the following way:

    operations = [
        migrations.RenameField(
            model_name='your_model_name',
            old_name='anticipated_end_date',
            new_name='tentative_end_date',
        ),
    ]

Now you can run python manage.py migrate.

This way your model field/DB column will be renamed, and your data will not be lost.

一杆小烟枪 2024-09-21 18:51:05

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

▶︎ 从以下内容开始:

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代码>

系统会提示您:

您是否将 MODEL.OLD_FIELD_NAME 重命名为 MODEL.NEW_FIELD_NAME(外键)? [是/否] 是

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

此过程适用于 Django 1.7+。

It is possible to rename a field without doing any manual migration file editing:

▶︎ Start with something like this:

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

▶︎ Add db_column=OLD_FIELD_NAME to the original field.

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

▶︎ Run: python3 manage.py makemigrations

▶︎ Rename the field from OLD_FIELD_NAME to NEW_FIELD_NAME

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

▶︎ Run: python3 manage.py makemigrations

You will be prompted:

Did you rename MODEL.OLD_FIELD_NAME to MODEL.NEW_FIELD_NAME (a ForeignKey)? [y/N] y

This will generate two migration files rather than just one, although both migrations are auto-generated.

This procedure works on Django 1.7+.

小草泠泠 2024-09-21 18:51:05

我在 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

I ran into this situation on Django 1.7.7. I ended up doing the following which worked for me.

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

Added a simple subclass of migrations.RenameField that doesn't touch the database:

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
倾听心声的旋律 2024-09-21 18:51:05

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

就我而言:

旧字段名称是:is_admin
新的字段名称是:is_superuser

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

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

UPDATE In Django 3.1 it is quite simple for changing only one field at a time.

In my case:

The old field name was: is_admin
The new field name was: is_superuser

When I make migrations by python manage.py makemigrations it asked me do I want to rename the field or not. And I just hit y to rename. Then I migrate by python manage.py migrate. The terminal history in my case looks like:
enter image description here

NOTE: I did not test with more than one field at a time.

演出会有结束 2024-09-21 18:51:05

正如其他响应中所指出的,现在可以很容易地使用 db_column 重命名字段,而无需对数据库进行任何更改。但生成的迁移实际上会创建一些SQL语句。您可以通过在迁移时调用 ./manage.py sqlmigrate ... 来验证这一点。

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

我写了一篇小文章 如果你想了解更多的话。

As pointed out in the other responses, it is now quite easy to rename a field with no changes on the database using db_column. But the generated migration will actually create some SQL statements. You can verify that by calling ./manage.py sqlmigrate ... on your migration.

To avoid any impact on your database you need to use SeparateDatabaseAndState to indicate to Django that it doesn't need to do something in DB.

I wrote a small article about that if you want to know more about it.

半暖夏伤 2024-09-21 18:51:05

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

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

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

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

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

  2. 应用迁移
    $ python 管理.py 迁移

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

1.Edit the field name on the django model

2.Create an empty migration like below:

$ python manage.py makemigrations --empty testApp (testApp is your application name)

  1. Edit the empty migration file which is created recently

    operations = [
    migrations.RenameField('your model', 'old field’, 'new field'),
    ]

  2. Apply the migration
    $ python manage.py migrate

Database column name will be altered with new name.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文