【问题标题】:Django-DB-Migrations: cannot ALTER TABLE because it has pending trigger eventsDjango-DB-Migrations:不能 ALTER TABLE 因为它有待处理的触发事件
【发布时间】:2012-10-02 00:46:14
【问题描述】:

我想从 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

怎么了?

【问题讨论】:

  • 这个问题很相似:stackoverflow.com/questions/28429933/… 并且有对我更有用的答案。
  • 我在 Postgres v10 上遇到了同样的问题(但在 Postgres v.12 上没有)。通过添加单独的迁移文件解决了问题。

标签: python django postgresql django-migrations


【解决方案1】:

另一个原因可能是因为您尝试将列设置为NOT NULL,而实际上它已经具有NULL 值。

【讨论】:

  • 要解决这个问题,您可以使用数据迁移或手动(manage.py shell)进入并更新不合规的值
  • @mgojohn 你是怎么做到的?
  • @pyramidface 如果你不太挑剔,你可以在 django shell 中更新 null 值。如果您正在寻找更正式和可测试的东西,这取决于您使用的版本。如果您使用 south,请参阅:south.readthedocs.org/en/latest/tutorial/part3.html,如果您使用 django 的迁移,请参阅此处的“数据迁移”部分:docs.djangoproject.com/en/1.8/topics/migrations
  • 你拯救了我的一天!
  • 我是 Django 的忠实粉丝,但显然像这样的错误消息 - 完全是红鲱鱼 - 只是显示了此类平台的复杂性!
【解决方案2】:

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

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

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

然后创建架构迁移:

manage.py schemamigration fooapp --auto

现在您有两个事务,分两步迁移应该可以工作。

【讨论】:

  • PostgreSQL 可能改变了它关于此类事务的行为,因为我设法在我的开发机器 (PostgreSQL 9.4) 上运行了数据和架构更改的迁移,而它在服务器 (PostgreSQL 9.1) 上失败了。
  • 对我来说几乎一样。直到今天,它完美地运行了 100 多次迁移(包括约 20 次数据迁移),同时添加了唯一的约束以及数据迁移删除之前的重复项。 PostgreSQL 10.0
  • 如果在迁移中使用 RunPython 操作进行数据迁移,您只需要确保它是最后一个操作即可。 Django 知道如果 RunPython 操作是最后一次,则打开自己的事务。
  • @Dougyfresh 这是 django 的文档化功能吗?
  • Django 的迁移还支持Migration.atomic = False,这使您不必吐出文件(如答案中所述)。
【解决方案3】:

在操作中我设置了 SET CONSTRAINTS:

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

【讨论】:

【解决方案4】:

如果要添加不可为空的字段,则需要分两次迁移:

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

说明

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

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

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

在支持 DDL 事务的数据库(SQLite 和 PostgreSQL)上,除了为每次迁移创建的事务外,RunPython 操作不会自动添加任何事务。因此,例如,在 PostgreSQL 上,您应该避免在同一迁移中组合架构更改和 RunPython 操作,否则您可能会遇到诸如 OperationalError: cannot ALTER TABLE "mytable" 因为它有待处理的触发事件之类的错误。

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

【讨论】:

    【解决方案5】:

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

    【讨论】:

    • 这个解决方案的问题是:如果你的迁移在db.commit_transaction()之后失败了怎么办?如果您需要,我更喜欢使用三个迁移:schema-mig、data-mig、schema-mig。
    • 参见:django.readthedocs.io/en/latest/ref/migration-operations.html 在支持 DDL 事务的数据库(SQLite 和 PostgreSQL)上,除了为每次迁移创建的事务之外,RunPython 操作不会自动添加任何事务。因此,例如,在 PostgreSQL 上,您应该避免在同一迁移中组合模式更改和 RunPython 操作,否则您可能会遇到诸如 OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events 之类的错误。
    【解决方案6】:

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

    问题是您无法同时更改尝试更新值的同一列架构。

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

    【讨论】:

    • 运行一些脚本对我来说并不是一个真正的选择。我有几个数据库实例,持续部署过程只调用“manage.py migrate”。这个问题已经是有效的答案,可以正常工作。
    【解决方案7】:

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

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

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

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

    问题解决了

    【讨论】:

      【解决方案8】:

      在我的情况下,我有

      1. 添加字段
      2. 运行Python
      3. 删除字段

      然后我只是将最后一个 RemoveFied 移动到新的迁移文件中,从而解决了问题

      【讨论】:

        猜你喜欢
        • 2017-08-06
        • 2018-11-06
        • 2015-07-03
        • 1970-01-01
        • 2020-05-07
        • 1970-01-01
        • 2011-03-29
        • 2016-04-28
        • 1970-01-01
        相关资源
        最近更新 更多