mysql 为什么Django在添加新列时删除SQL DEFAULT约束?

k5hmc34c  于 12个月前  发布在  Mysql
关注(0)|答案(2)|浏览(114)

在最新的Django(2.2)中,当我像这样向模型添加一个新字段时:

new_field= models.BooleanField(default=False)

字符串
Django为MySQL运行以下命令:

ALTER TABLE `app_mymodel` ADD COLUMN `new_field` bool DEFAULT b'0' NOT NULL;
ALTER TABLE `app_mymodel` ALTER COLUMN `new_field` DROP DEFAULT;
COMMIT;


虽然这在所有内容都更新时有效,但这是非常有问题的,因为旧版本的应用程序在运行此迁移后无法再创建模型(它们不知道new_field)。

yhuiod9q

yhuiod9q1#

为什么不保留DEFAULT约束?
因为Django在应用程序级别处理default模型字段选项,而不是数据库级别。所以真实的问题是为什么它设置DEFAULT约束。
关于第一点,历史上Django不支持模型字段的数据库级默认值。总是有一些人对改变这一点感兴趣(关于这个主题的first issue已经有18年的历史了),当然其他框架(Rails,我想,和SQLAlchemy)表明这是可能的。
最近,Django添加了一个名为db_default的单独字段选项。default的行为保持不变,并且在应用程序级别处理默认值有很好的理由。例如:表达任意复杂计算的能力;不必担心数据库引擎之间的微妙不兼容性;在代码中示例化新示例并立即访问默认值的能力;在表单中向用户呈现默认值的能力;等等。
现在,向现有数据库添加一个新的不可空字段是一个非常不同的用例。在这种情况下,您必须为数据库提供一个默认值,以便它执行操作。makemigrations将尝试从您的default选项中推断正确的值,如果可以的话,如果没有,它会强制你从命令行指定一个值。所以DEFAULT修饰符被用于这个有限的目的,然后被删除。
正如你所注意到的,Django中缺少数据库级别的默认值会使持续部署变得更加困难。但是解决方案相当简单:只需在迁移中重新添加默认值。迁移系统的一个巨大好处是,它可以轻松地在Django的ORM之外对数据库进行任意,可重复,可测试的更改。所以只需添加一个新的RunSQL迁移操作:

operations = [
    # Add SQL for both forward and reverse operations
    migrations.RunSQL("ALTER TABLE app_mymodel ALTER COLUMN new_field SET DEFAULT 0;",
                      "ALTER TABLE app_mymodel ALTER COLUMN new_field DROP DEFAULT;")
]

字符串
您可以将其放入新的迁移文件中,或者简单地编辑自动生成的文件。根据您的数据库及其对事务性迁移的支持,操作序列可能是原子的,也可能不是原子的。

7y4bm7vi

7y4bm7vi2#

我发现这张票从2年前:https://code.djangoproject.com/ticket/28000
其中指出:

**Django使用数据库默认值来设置表中现有行的值。它不会将默认值保留在数据库中,因此删除默认值是正确的行为。**在这种情况下,可能有一个优化,即不设置/删除默认值-我不确定是否需要,因为该列不是null。可以为此打开一个单独的工单。

我在这里的另一个问题中也看到了同样的引用:Django Postgresql dropping column defaults at migrate
再搜索一点,我发现了这个SO问题:Django implementation of default value in database,它导致了_alter_field方法的代码,其中存在以下注解:

# When changing a column NULL constraint to NOT NULL with a given
# default value, we need to perform 4 steps:
#  1. Add a default for new incoming writes
#  2. Update existing NULL rows with new default
#  3. Replace NULL constraint with NOT NULL
#  4. Drop the default again.

字符串
虽然最后一个是关于将现有的可空字段更改为不可空字段,但这似乎是Django处理default情况的方式:

相关问题