Django未正确迁移带有choices的CharField。

huangapple go评论54阅读模式
英文:

Django not migrating CharField with choices properly

问题

我有一个名为`Car`的模型,它有一个`color`的CharField字段,可以选择`white`、`black`和`blue`。

```python
class Car(models.Model):

    WHITE = "White"
    BLACK = "Black"
    BLUE = "Blue"

    COLOR_CHOICES = (
        (WHITE, "White"),
        (BLACK, "Black"),
        (BLUE, "Blue"),
    )

    ...

    color = models.CharField(max_length=20, choices=COLOR_CHOICES, default=BLUE)

我已经创建了带有颜色的Car对象。现在,当我对选择进行更改(例如,在所有情况下将BLUE更改为RED),并运行迁移时,已经存在的颜色为BLUE的Car对象不会迁移到RED。这就是奇怪之处:

  1. 当我使用django shell检查这些对象时,它们显示为Car.BLUE(不再存在的选择)。
  2. 当我在Django管理界面中检查这些对象时,它们显示为Car.WHITE。
  3. 当我创建一个新对象时,它可以正常工作 - 它会自动变成Car.RED(选择了默认值)。

问题:

  • 在迁移CharField的选择时,是否有任何特定的步骤我忽略了?
  • 为什么会出现这种奇怪的行为,如何安全地修复?
  • 我是否需要手动(或通过脚本)修复数据?

我期望在迁移时,所有现有的Car.BLUE对象都会迁移到Car.RED,因为这是新的默认值。我尝试手动创建一个新对象,它会选择新的默认的RED颜色。我还尝试手动将现有对象从BLUE更改为RED,这也可以正常工作。应用迁移时没有出现错误。

Django版本 - 4.1.5
数据库 - PostgreSQL


<details>
<summary>英文:</summary>

I have a model `Car` with a CharField `color`, that has the choices of `white`, `black` and `blue`.

class Car(models.Model):

WHITE = &quot;White&quot;
BLACK = &quot;Black&quot;
BLUE = &quot;Blue&quot;

COLOR_CHOICES = (
    (WHITE, &quot;White&quot;),
    (BLACK, &quot;Black&quot;),
    (BLUE, &quot;Blue&quot;),
)

...

color = models.CharField(max_length=20, choices=COLOR_CHOICES, default=BLUE)

I already have created `Car` objects with a color. Now when I introduce a change to the choices (e.g. change BLUE to RED in all occurences, as well as in the default) and run the migrations, the `Car` objects of color BLUE that already exist **do not** get migrated to RED. And that&#39;s where the weirdness begins:
1. When I use the django shell to check the objects, they appear as Car.BLUE (the choice that no longer exists). 
2. When I inspect the objects in the Django Admin, they appear as Car.WHITE.
3. When I create a new object, it works - it becomes Car.RED automatically (picks up the default).

Questions:
- Are there any specific steps that I missed when migrating choices for the CharField?     
- Why could this weird behavior be happening and how can I safely fix?
- Do I have to manually (or through a script) fix the data?

I expect upon migration all existing Car.BLUE objects to migrate to Car.RED when I run the migrations, because this is the new default value. I tried to manually create a new object and it picks up the new default RED color. I also tried to manually change an existing object from BLUE to RED, which is also fine. There were no errors when applying the migrations.

*Django version - 4.1.5
DB - PostgreSQL*

</details>


# 答案1
**得分**: 0

Django如果您更改[**<code>choices=&hellip;</code>**参数](https://docs.djangoproject.com/en/stable/ref/models/fields/#choices),实际上Django将进行迁移,实际上如果您更改任何参数,Django都将进行迁移。但是默认的迁移处理程序…在您更改`choices`时不会执行任何操作。可以实现不同的处理程序,但可能不值得努力。

这意味着最终不会执行任何数据库更改,因此我们必须自己执行这些操作。如果要修改数据,您可以创建[*数据迁移*](https://docs.djangoproject.com/en/stable/topics/migrations/#data-migrations)。数据迁移与普通迁移类似,只是我们编写数据库应如何更改的方式。

我们使用以下方式创建数据迁移:

```python manage.py <strong>makemigrations --empty</strong> <em>app_name</em>```

在迁移文件中,然后我们可以构建一个更新查询:

```python
from django.db import migrations


def <b>red_to_blue</b>(apps, schema_editor):
    Car = apps.get_model('<em>app_name</em>', 'Car')
    Car.objects.filter(color='Red')<b>.update(color='Blue')</b>


class Migration(migrations.Migration):
    dependencies = [
        ("<em>app_name</em>", "<em>1234_some_migration</em>"),
    ]

    operations = [
        migrations.RunPython(<b>red_to_blue</b>),
    ]

然后进行迁移,它将运行red_to_blue函数,并将数据库中的数据迁移。

英文:

Django will make migrations if you change the <code>choices=&hellip;</code> parameter&nbsp;<sup>[Django-doc]</sup>, in fact Django will make a migration if you change any parameter. But the default migration handler&hellip; will not do anything when you change the choices. One can implement a different handler, but likely it is not worth the effort.

This thus means that eventually no database changes will be done, so we will have to do this ourselves. If you want to modify data, you can make a data migration&nbsp;<sup>[Django-doc]</sup>. A data migration is just like a normal migration, except that we write how the database should change.

We make a data migration with:

<pre><code>python manage.py <strong>makemigrations --empty</strong> <em>app_name</em></code></pre>

In the migration file, we can then construct an update query:

<pre><code>from django.db import migrations

def <b>red_to_blue</b>(apps, schema_editor):
Car = apps.get_model('<em>app_name</em>', 'Car')
Car.objects.filter(color='Red')<b>.update(color='Blue')</b>

class Migration(migrations.Migration):
dependencies = [
(&quot;<em>app_name</em>&quot;, &quot;<em>1234_some_migration</em>&quot;),
]

operations = [
    migrations.RunPython(&lt;b&gt;red_to_blue&lt;/b&gt;),
]&lt;/code&gt;&lt;/pre&gt;

If we then migrate, it will run the red_to_blue function, and migrate the data in the database.

huangapple
  • 本文由 发表于 2023年6月12日 21:08:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76456995.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定