【问题标题】:Adding a non-nullable field on a production Django deployment在生产 Django 部署中添加不可为空的字段
【发布时间】:2018-07-20 18:26:09
【问题描述】:

我有一个带有 PostgreSQL 数据库的生产 Django 部署 (Django 1.11)。我想在我的一个模型中添加一个不可为空的字段:

class MyModel(models.Model):
    new_field = models.BooleanField(default=False)

为了进行部署,我需要更新服务器上的代码或先运行迁移,但由于这是一个生产部署,请求可能(并且将会)在我更新数据库和更新服务器之间发生。如果我先更新服务器,我会得到一个OperationalError no such column,所以我显然需要先更新数据库。

但是,当我首先更新数据库时,在使用新代码更新之前,服务器上发出的请求出现以下错误:

django.db.utils.IntegrityError: NOT NULL 约束失败:myapp_mymodel.new_field

从表面上看,这没有任何意义,因为该字段具有默认值。进一步深入研究,似乎默认值是由 Django 逻辑单独提供的,而不是实际存储在 SQL 级别上。如果服务器没有更新的代码,它不会将该列传递给 SQL 进行更新,SQL 将其解释为 NULL。

鉴于此,我如何将这个新的不可为空的字段部署到我的应用程序而不让我的用户收到任何错误?

【问题讨论】:

    标签: django deployment django-migrations


    【解决方案1】:

    迁移应始终在部署开始时运行,否则您会遇到其他问题。此问题的解决方案是将更改拆分为两个部署。

    在部署 1 中,该字段必须可以为空(NullBooleanFieldnull=True)。您应该对处于此状态的代码进行迁移,并确保如果该字段的值为 None,其余代码不会崩溃。这是必要的,因为请求可以发送到还没有新代码的服务器;如果这些服务器创建模型的实例,它们将创建它,并且字段为空。

    在部署 2 中,您将字段设置为不可为空,为此进行迁移,并删除您为处理字段值为 None 的情况而编写的任何额外代码。如果该字段没有默认值,则您为第二次部署所做的迁移将需要填写此字段中具有 None 的对象的值。

    也需要这两种部署技术来安全地删除字段,尽管它看起来有点不同。在第一次部署中,您从模型文件中删除了该字段,并从您的代码中删除了对它的所有引用,但是您部署了默认迁移来删除该字段。相反,部署 1 具有将字段设置为可为空的自定义迁移。然后,在部署 2 中,您部署迁移以实际从数据库中删除该字段。

    【讨论】:

      【解决方案2】:

      您可以通过以NullBooleanField 开头来完成此操作:

      1. new_field = models.NullBooleanField(default=False) 添加到您的模型中
      2. 使用 makemigrations 创建架构迁移 1
      3. 将模型更改为具有new_field = models.BooleanField(default=False)
      4. 使用 makemigrations 创建架构迁移 2
      5. 运行架构迁移 1
      6. 更新生产代码
      7. 运行架构迁移 2

      如果旧的生产代码在第 5 步和第 6 步之间写入表,则会写入空值 new_field。在第 6 步和第 7 步之间会有一段时间,BooleanField 可以有空值,当读取该字段时,它将为空。如果你的代码可以处理这个问题,你会没事的,然后第 7 步会将所有这些空值转换为 False。如果您的新代码无法处理这些空值,您可以执行以下步骤:

      1. new_field = models.NullBooleanField(default=False) 添加到您的模型中
      2. 使用 makemigrations 创建架构迁移 1
      3. 运行架构迁移 1
      4. 更新生产代码
      5. 将模型更改为具有new_field = models.BooleanField(default=False)
      6. 使用 makemigrations 创建架构迁移 2
      7. 运行架构迁移 2
      8. 更新生产代码

      *请注意,这些方法仅使用 Postgres 进行了测试。

      【讨论】:

      • 我建议将第 4 步和第 6 步结合起来。无论如何,作为在迁移中使字段非空的一部分,Django 会强制您执行此操作。
      【解决方案3】:

      通常,django 升级过程如下所示:

      本地开发环境:

      • 在本地更改模型
      • 迁移模型(python manage.py makemigrations)
      • 在本地测试您的更改
      • 提交您的更改并将其推送到 (git) 服务器

      在生产服务器上:

      • 设置 ENV 参数
      • 从您的版本控制系统中提取(git fetch --all; git reset --hard origin/master)
      • 更新 python 依赖项(例如 pip install -r requirements.txt)
      • 迁移(manage.py migrate_schemas)
      • 更新静态文件(python manage.py collectstatic)
      • 重新启动 django 服务器(取决于服务器,但可能类似于“python manage.py runserver”)

      【讨论】:

      • 嗨戴维。我不是在询问 django 项目的一般部署过程(您描述的基本上已经是我的部署过程)。我正在寻找一种方法来部署这个不会对我的用户造成错误的新布尔字段。
      • 您可以将站点置于维护模式:github.com/fabiocaccamo/django-maintenance-mode。但是在服务器重新启动期间,用户仍然会收到错误消息
      • 我已经澄清了这个问题。我想要一个无错误的部署
      猜你喜欢
      • 2016-08-09
      • 1970-01-01
      • 2015-08-22
      • 2013-12-11
      • 1970-01-01
      • 1970-01-01
      • 2021-03-22
      • 2015-02-21
      • 2017-03-05
      相关资源
      最近更新 更多