我有一个带有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。

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

最佳答案

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

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

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

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

关于django - 在生产Django部署中添加非空字段,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51448420/

10-09 07:17
查看更多