使用SeparateDatabaseAndState删除不可为空的Django字段

问题描述 投票:0回答:1

假设我有以下型号:

class Product(models.Model):
    name = models.CharField(max_length=128)
    is_retired = models.BooleanField(default=False)

我想删除

is_retired
字段。我正在使用蓝绿部署来发布对生产的更改,因此我在迁移中使用 SeparateDatabaseAndState

我从应用程序状态中删除该字段的初始迁移很简单:

class Migration(migrations.Migration):
    dependencies = ...
    operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(
                    model_name="product",
                    name="is_retired",
                ),
            ],
            database_operations=[],
        ),
    ]

我可以成功运行迁移。但是,当我尝试使用

Product.objects.create(name="Wrench")
创建新产品时,出现以下错误:

self = <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x106bf4050>
query = 'INSERT INTO "core_product" ("name") VALUES (?) RETURNING "core_product"."id"'
params = ('Wrench',)

    def execute(self, query, params=None):
        if params is None:
            return super().execute(query)
        # Extract names if params is a mapping, i.e. "pyformat" style is used.
        param_names = list(params) if isinstance(params, Mapping) else None
        query = self.convert_query(query, param_names=param_names)
>       return super().execute(query, params)
E       django.db.utils.IntegrityError: NOT NULL constraint failed: core_product.is_retired

看起来

INSERT
查询失败,因为
is_retired
字段默认为
null
,而不是正确的默认值
False

我通过在将

Product.is_retired
从状态中删除之前使其可为空来修复它:

class Migration(migrations.Migration):
    dependencies = ...
    operations = [
        migrations.AlterField(
            model_name="product",
            name="is_retired",
            field=models.BooleanField(default=False, null=True),
        ),
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(
                    model_name="product",
                    name="is_retired",
                ),
            ],
            database_operations=[],
        ),
    ]

现在我可以使用

Product.objects.create(name="Wrench")
成功创建产品了。

几个问题:

  • 这是修复此错误的最佳方法吗?还有其他想法吗?我考虑将
    is_retired
    默认值更改回
    False
    ,但无法弄清楚。
  • 在蓝绿部署期间将字段从不可为空更改为可空是否安全?我很确定是这样,但只是想确定一下。
django django-migrations blue-green-deployment sqlmigrations
1个回答
0
投票

在将 is_retired 字段从状态中删除之前将其设置为空的方法是解决您遇到的问题的有效方法。不过,让我们直接解决您的问题:

这是修复此错误的最佳方法吗?还有其他想法吗?

你的做法是明智的。通过在将字段从状态中删除之前将其设置为可为空,可以避免在插入新记录时出现 NOT NULL 约束失败。另一种方法可能涉及在运行迁移之前手动设置数据库中字段的默认值以将其从状态中删除,但这可能涉及更多手动数据库操作,并且可能容易出错。

在蓝绿部署期间将字段从不可为空更改为可空是否安全?

将字段从不可为空更改为可空通常应该是安全的,特别是如果您的数据库可以正常处理空值。就您而言,由于您使用的是蓝绿部署策略,因此可以进一步降低风险,因为您可以在将流量切换到单独的环境之前测试更改。但是,彻底测试此类更改以确保它们不会对应用程序的行为产生意外后果始终是一个好习惯。

© www.soinside.com 2019 - 2024. All rights reserved.