我有一个带有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。
鉴于此,如何在我的用户没有任何错误的情况下将这个新的布尔字段部署到我的应用程序中?
答案 0 :(得分:2)
迁移应始终在部署开始时运行,否则会遇到其他问题。解决此问题的方法是将更改分为两个部署。
在部署1中,该字段必须为空(NullBooleanField
或null=True
)。您应该在这种状态下对代码进行迁移,并确保如果字段的值为None
,其余代码不会崩溃。这是必要的,因为请求可以发送到尚不具有新代码的服务器。如果这些服务器创建了模型的实例,则将在字段为null的情况下创建模型。
在部署2中,将字段设置为不可为空,为此进行迁移,并删除为处理字段值为None
而编写的所有多余代码。如果该字段没有默认值,则您为第二次部署进行的迁移将需要填写该字段中具有None
的对象的值。
还需要两种部署技术来安全删除字段,尽管看起来有些不同。在第一次部署中,您从模型文件中删除了该字段,并从代码中删除了对该字段的所有引用,但是您不部署默认迁移以删除该字段。而是,部署1具有将字段设置为可空的自定义迁移。然后,在部署2中,部署迁移以实际从数据库中删除该字段。
答案 1 :(得分:1)
您可以通过以NullBooleanField
开头:
new_field = models.NullBooleanField(default=False)
添加到模型中new_field = models.BooleanField(default=False)
如果旧的生产代码在第5步和第6步之间写入表,则将写入空值new_field
。在第6步和第7步之间会有一个时间,其中BooleanField可以为空值,并且在读取该字段时,该值为空。如果您的代码可以解决这个问题,那么您就可以了,然后,步骤7将所有这些空值转换为False。如果您的新代码无法处理这些空值,则可以执行以下步骤:
new_field = models.NullBooleanField(default=False)
添加到模型中new_field = models.BooleanField(default=False)
*请注意,这些方法仅在Postgres中进行了测试。
答案 2 :(得分:-1)
通常,Django升级过程如下:
本地开发环境:
在生产服务器上: