在生产Django部署中添加布尔字段

时间:2018-07-20 18:26:09

标签: django deployment django-migrations

我有一个带有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会将其解释为NULL。

鉴于此,如何在我的用户没有任何错误的情况下将这个新的布尔字段部署到我的应用程序中?

3 个答案:

答案 0 :(得分:2)

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

在部署1中,该字段必须为空(NullBooleanFieldnull=True)。您应该在这种状态下对代码进行迁移,并确保如果字段的值为None,其余代码不会崩溃。这是必要的,因为请求可以发送到尚不具有新代码的服务器。如果这些服务器创建了模型的实例,则将在字段为null的情况下创建模型。

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

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

答案 1 :(得分:1)

您可以通过以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中进行了测试。

答案 2 :(得分:-1)

通常,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”)