Django-DB-Migrations:不能ALTER TABLE,因为它有未决的触发事件

时间:2012-10-11 11:06:29

标签: python django postgresql django-migrations

我想从TextField中删除null = True:

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

我创建了一个架构迁移:

manage.py schemamigration fooapp --auto

由于某些页脚列包含NULL,如果我运行迁移,我会得到此error

  

django.db.utils.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

有什么问题?

7 个答案:

答案 0 :(得分:103)

每次迁移都在交易中。在PostgreSQL中,您不能更新表,然后在一个事务中更改表模式。

您需要拆分数据迁移和架构迁移。首先使用以下代码创建数据迁移:

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

然后创建架构迁移:

manage.py schemamigration fooapp --auto

现在你有两个交易,两个步骤的迁移应该有效。

答案 1 :(得分:103)

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

答案 2 :(得分:11)

在操作中,我设置了SET约束:

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

答案 3 :(得分:9)

刚刚遇到这个问题。您还可以在架构迁移中使用db.start_transaction()和db.commit_transaction()将数据更改与架构更改分开。可能不是那么干净,没有单独的数据迁移,但在我的情况下,我需要架构,数据,然后另一个架构迁移,所以我决定一次完成所有。

答案 4 :(得分:1)

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

问题是您无法更改试图同时更新其值的列模式。

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

答案 5 :(得分:1)

在PostgreSQL和SQLite上,如果在同一迁移中结合了架构更改的RunPython命令足够复杂,则会发生此问题。例如,如果要添加一个不可为空的字段,则典型的迁移步骤为:

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

在SQLite和Postgres上,这可能会引起问题,因为整个事情是在一个事务中完成的。
Django docs对此有特定警告:

在支持DDL事务的数据库(SQLite和PostgreSQL)上,RunPython操作除了为每个迁移创建的事务外,没有自动添加任何事务。因此,例如,在PostgreSQL上,应避免在同一迁移中结合使用模式更改和RunPython操作,否则您可能会遇到诸如OperationalError的错误:不能ALTER TABLE“ mytable”,因为它有未决的触发事件。

在这种情况下,解决方案是将您的迁移分为多个迁移。通常,拆分的方法是进行第一次迁移,其中包含通过run_python命令进行的逐步升级,而第二次迁移则包含其后的所有迁移。因此,在上述情况下,模式将在一次迁移中为AddFieldRunPython,在第二次迁移中为AlterField

答案 6 :(得分:0)

<块引用>

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

<块引用>

第 2 步)然后再次进行迁移和迁移

<块引用>

step 3)最后再添加第一步中删除的字段

<块引用>

第 4 步)然后再次进行迁移和迁移

问题已解决