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

gue*_*tli 103 python django postgresql django-migrations

我想从TextField中删除null = True:

-    footer=models.TextField(null=True, blank=True)
+    footer=models.TextField(blank=True, default='')
Run Code Online (Sandbox Code Playgroud)

我创建了一个模式迁移:

manage.py schemamigration fooapp --auto
Run Code Online (Sandbox Code Playgroud)

由于一些页脚列包含NULL我得到这个,error如果我运行迁移:

django.db.utils.IntegrityError:列"footer"包含空值

我将此添加到架构迁移中:

    for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
        sender.footer=''
        sender.save()
Run Code Online (Sandbox Code Playgroud)

现在我得到:

django.db.utils.DatabaseError: cannot ALTER TABLE "fooapp_emailsender" because it has pending trigger events
Run Code Online (Sandbox Code Playgroud)

怎么了?

maa*_*zza 122

另一个原因可能是因为您尝试将列设置为NOT NULL实际已有NULL值的列.

  • 要解决此问题,您可以使用数据迁移或手动(manage.py shell)进入并更新不符合要求的值 (6认同)
  • 我是 Django 的忠实粉丝,但显然像这样的错误消息 - 完全是转移注意力 - 只是显示了此类平台的复杂性! (2认同)

gue*_*tli 114

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

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

 for sender in orm['fooapp.EmailSender'].objects.filter(footer=None):
    sender.footer=''
    sender.save()
Run Code Online (Sandbox Code Playgroud)

然后创建架构迁移:

manage.py schemamigration fooapp --auto
Run Code Online (Sandbox Code Playgroud)

现在您有两个事务,并且两个步骤中的迁移应该有效.

  • PostgreSQL可能改变了有关此类事务的行为,因为我设法在我的开发机器(PostgreSQL 9.4)上运行了一次数据和架构更改的迁移,而它在服务器上失败了(PostgreSQL 9.1). (7认同)
  • Django 的迁移还支持 `Migration.atomic = False`,它允许您不必吐出文件(如答案中所述)。 (3认同)
  • 如果在迁移中使用 RunPython 操作进行数据迁移,您只需确保它是最后一个操作。Django 知道如果 RunPython 操作是最后一个,则打开自己的事务。 (2认同)
  • @Dougyfresh 这是 django 的记录功能吗? (2认同)
  • @jnns 谢谢,将原子设置为“False”解决了我的问题 (2认同)

slu*_*uge 19

在操作中,我设置了 SET CONSTRAINTS:

operations = [
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE;'),
    migrations.RunPython(migration_func),
    migrations.RunSQL('SET CONSTRAINTS ALL DEFERRED;'),
]
Run Code Online (Sandbox Code Playgroud)

  • 最好使用[SeparateDatabaseAndState](https://docs.djangoproject.com/en/dev/ref/migration-operations/#separatedatabaseandstate) (2认同)

Zag*_*ags 12

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

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

说明

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

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

在 SQLite 和 Postgres 上,这可能会导致问题,因为整个事情都在一个事务中完成。
Django文档对此有一个特定的警告:

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

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


cli*_*ime 9

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

  • 此解决方案的问题是:如果迁移在db.commit_transaction()之后失败,会发生什么?如果您需要,我更喜欢使用三次迁移:schema-mig,data-mig,schema-mig. (7认同)
  • 请参阅:http://django.readthedocs.io/en/latest/ref/migration-operations.html在支持DDL事务的数据库(SQLite和PostgreSQL)上,RunPython操作除了为其创建的事务之外没有自动添加任何事务每次迁移.因此,例如,在PostgreSQL上,您应该避免在同一次迁移中组合模式更改和RunPython操作,否则您可能会遇到类似OperationalError的错误:不能ALTER TABLE"mytable",因为它有待处理的触发事件. (5认同)