Django-DB-迁移: 无法 ALTERTABLE,因为它有挂起的触发器事件

我想从 TextField 中移除 null = True:

-    footer=models.TextField(null=True, blank=True)
+    footer=models.TextField(blank=True, default='')

我创造了一个模式迁移:

manage.py schemamigration fooapp --auto

因为有些页脚列包含 NULL,所以如果我运行迁移,就会得到这个 error:

IntegrityError: 列“ footer”包含空值

我在模式迁移中加入了以下内容:

    for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
sender.footer=''
sender.save()

现在我明白了:

django.db.utils.DatabaseError: cannot ALTER TABLE "fooapp_emailsender" because it has pending trigger events

怎么了?

70027 次浏览

每次迁移都在一个事务中。在 PostgreSQL 中,不能更新表,然后在一个事务中更改表架构。

你需要把数据迁移和模式迁移分开,首先用下面的代码创建数据迁移:

 for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
sender.footer=''
sender.save()

然后创建模式迁移:

manage.py schemamigration fooapp --auto

现在您有了两个事务,两个步骤的迁移应该可以正常工作。

刚刚碰到了这个问题。您还可以在模式迁移中使用 db.start _ action ()和 db.commit _ action ()将数据更改与模式更改分开。可能不会干净到有一个单独的数据迁移,但在我的情况下,我需要模式、数据,然后是另一个模式迁移,所以我决定一次性完成。

这样做的另一个原因可能是,当一个列实际上已经有 NULL值时,您尝试将该列设置为 NOT NULL

在操作中,我设置了限制条件:

operations = [
migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
migrations.RunPython(migration_func),
migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;'),
]

您正在更改列架构。该脚注列不能再包含空值。该列的数据库中很可能已经存储了空值。Django 将使用洄游命令将数据库中的空行从空行更新为现在的默认值。Django 尝试更新页脚列中有空值的行,并同时更改模式(我不确定)。

问题是,您不能同时更改要更新值的同一列模式。

一种解决方案是删除更新模式的迁移文件。然后,运行脚本将所有这些值更新为默认值。然后重新运行迁移以更新模式。这样,更新就已经完成了。Django 迁移只是改变了模式。

如果要添加一个非空字段,则需要进行两次迁移:

  1. AddFieldRunPython来填充它
  2. AlterField将字段更改为不可为空

解释

在 PostgreSQL 和 SQLite 上,如果在同一迁移中使用足够复杂的 RunPython命令并结合模式更改,则可能出现此问题。例如,如果要添加一个非空字段,典型的迁移步骤是:

  1. AddField添加可为空的字段
  2. RunRython来填充它
  3. AlterField将字段更改为不可为空

在 SQLite 和 Postgres 上,这可能会导致问题,因为整个事务都是在一个事务中完成的。
姜戈医生对此有一个明确的警告:

在支持 DDL 事务的数据库(SQLite 和 PostgreSQL)上,除了为每次迁移创建的事务之外,RunPython 操作没有自动添加任何事务。因此,例如,在 PostgreSQL 上,应该避免在同一个迁移中将模式更改和 RunPython 操作结合起来,否则可能会碰到类似 OperationalError: can not ALTER TABLE“ mytable”这样的错误,因为它有挂起的触发器事件。

如果是这种情况,解决方案是将您的迁移分成多个迁移。通常,分割的方法是进行第一次迁移,其中包含通过 run _ python 命令向上的步骤,第二次迁移包含之后的所有步骤。因此,在上面描述的情况下,模式是一次迁移中的 AddFieldRunPython,第二次迁移中的 AlterField

步骤1)解决方案是从迁移文件夹中删除最新的迁移,并删除模型中最新添加的字段。

步骤2)然后再移民再移民

步骤3)最后再次添加在第一步中被删除的字段

步骤4)然后再移民再移民

问题解决了

对我来说

  1. AddField
  2. RunPython
  3. RemoveField

然后我将最后一个 RemoveFied 移动到新的迁移文件中,修复了这个问题