在django sql迁移中维护数据库默认值(而不是删除它们)。

django-postgres-dbdefaults的Python项目详细描述


支持数据库默认值的django.db.backends.postgresql的克隆。用于django 1.9.x.

动机-通过滚动web服务器更新更好地支持数据库迁移

django迁移(django 1.7中的新功能)不会将默认值插入数据库。我希望数据库默认值允许我在数据库仍在运行旧版本的web应用程序时使用新的迁移更新数据库。例如,我的大多数更新都遵循以下一般模式:

  1. 从我的生产负载平衡器中删除一台计算机
  2. 为所有生产Web服务器使用的数据库运行最新的数据库迁移
  3. 在我的计算机上更新负载平衡器之外的代码,并验证在该服务器上是否可以直接工作
  4. 用最新代码更新其余的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=falsedefault='的新字符字段导致插入错误等问题。

我很想听听人们对这种方法的看法,它似乎在过去曾引起过争议?在我对它的有限使用中,它似乎解决了我的需求,但是我还没有看到在更复杂的场景中会发生什么,比如对默认使用callable(imo不支持特定场景是合理的)。

关于django开发人员的一些讨论:

另一种想法是,它可能会为迁移添加更多的标志,从而允许更深入的定制,但对于任何框架来说,更多的标志和功能膨胀绝对是危险的事情。

不管我最终使用的是什么,在开发人员将迁移推送到我们的发布分支之前,我已经建立了一个更严格的工作流来审查迁移。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
IntelliJ中的java默认Maven项目结构不一致   java我希望链接(在帖子和页面上)在一些访问者加载时被自动点击   java如何使用单独的方法隐藏JButton并在新类中调用   java KStream leftJoin KStream具有相同的密钥   java图像在垂直滚动窗格视图端口中消失   java从指定的起始点开始以n的增量填充数组   java JLabel和JTextField不在swing表单中应用   java springboot mongo如果没有映像,请使用现有映像   类似C++映射的java容器   java如何在没有Valgrind错误的情况下调用JNI_CreateJavaVM?   java如何在安卓中运行后台服务   java onPostExecute不运行