在django sql迁移中维护数据库默认值(而不是删除它们)。
django-postgres-dbdefaults的Python项目详细描述
支持数据库默认值的django.db.backends.postgresql的克隆。用于django 1.9.x.
动机-通过滚动web服务器更新更好地支持数据库迁移
django迁移(django 1.7中的新功能)不会将默认值插入数据库。我希望数据库默认值允许我在数据库仍在运行旧版本的web应用程序时使用新的迁移更新数据库。例如,我的大多数更新都遵循以下一般模式:
- 从我的生产负载平衡器中删除一台计算机
- 为所有生产Web服务器使用的数据库运行最新的数据库迁移
- 在我的计算机上更新负载平衡器之外的代码,并验证在该服务器上是否可以直接工作
- 用最新代码更新其余的Web服务器
这将在数据库更新和最新代码之间创建延迟,如果列不为空且数据库中未设置其默认值,则可能导致在此期间数据库插入失败。
有些人建议创建一个附加的数据迁移脚本来手工添加默认值,这将有助于解决这个问题,但是(a)这很容易被忽略,并且(b)我猜想这会创建一个简短的时间窗口,该列在没有缺省情况下存在,并且仍然可能导致一个失败的插入。此外,一些[现有文档](https://docs.djangoproject.com/en/1.7/topics/migrations/#postgresql)是模糊的,并不能清楚地表明数据库默认值不包含在迁移中。
溶液
对于django.db.backends.postgresql模块,我注意到它在创建列时实际上会在db中添加默认值,但随后会在单独的sql语句中删除它们。因此,我创建了模块的一个子类,该子类重写删除默认值的一个字符串,并用no-op替换它(这是一个公认的黑客攻击,但似乎是最简单的方法,比复制粘贴原始类的大部分内容更能保存更新)。
例如,postgresql可能为迁移生成sql,如下所示:
$ python3 manage.py sqlmigrate app 0010 BEGIN; ALTER TABLE "my_table" ALTER COLUMN "some_column" SET DEFAULT false; ALTER TABLE "my_table" ALTER COLUMN "some_column" DROP DEFAULT; COMMIT;
通过此更改,相同的迁移脚本将第二个命令转换为no op:
$ python3 manage.py sqlmigrate app 0010 BEGIN; ALTER TABLE "my_table" ALTER COLUMN "some_column" SET DEFAULT false; ALTER TABLE "my_table" DROP COLUMN IF EXISTS skip_django_drop_default_feature RESTRICT; COMMIT;
此外,无需担心打算执行drop default的迁移,因为django迁移代码已经没有在sql中执行任何此类更改,它实际上看起来如下:
$ python3 manage.py sqlmigrate app 0011 BEGIN; -- -- Alter field some_column on my_table -- COMMIT;
讨论
我已经在Ringly.com上使用了一年多,并且安全地避免了在某些应用服务器使用新代码而其他服务器仍使用旧代码时,遇到诸如null=false和default='的新字符字段导致插入错误等问题。
我很想听听人们对这种方法的看法,它似乎在过去曾引起过争议?在我对它的有限使用中,它似乎解决了我的需求,但是我还没有看到在更复杂的场景中会发生什么,比如对默认使用callable(imo不支持特定场景是合理的)。
关于django开发人员的一些讨论:
- [数据库中的默认值?](https://groups.google.com/forum/#!searchin/django-developers/database$20defaults/django-developers/aQtt9fKHvjM/H59CaQycDSsJ)-2006年11月13日-0回复
- [默认的sql(postgres)](https://groups.google.com/forum/#!searchin/django-developers/database$20defaults/django-developers/fHjzttZTkzc/oXbrpBa0dHAJ)-2007年10月23日-4个回复
另一种想法是,它可能会为迁移添加更多的标志,从而允许更深入的定制,但对于任何框架来说,更多的标志和功能膨胀绝对是危险的事情。
不管我最终使用的是什么,在开发人员将迁移推送到我们的发布分支之前,我已经建立了一个更严格的工作流来审查迁移。