Django使用RunPython进行迁移以提交更改 [英] Django migrations using RunPython to commit changes

查看:393
本文介绍了Django使用RunPython进行迁移以提交更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我删除了一个我的模型中的一个外键,可以使用NULL值, null = True 从我的字段运行 makemigrations



因为我是一个改变一个已经在该字段中包含NULL值的行的表,我被要求立即提供一次性值或编辑迁移文件,并添加一个 RunPython 操作。 / p>

我的RunPython操作在 AlterField 操作之前列出,并对此字段执行所需的更新,因此它不包含NULL值(只有已经包含NULL值的行)。



但是,迁移仍然失败,出现此错误:
django。 db.utils.OperationalError:不能ALTER TABLEmy_app_site,因为它有待处理的触发事件



这是我的代码:

 # -  *  - 编码:utf-8  -  *  -  
从__future__导入unicode_literals

从django.db导入模型,迁移

def add_default_template(apps,schema_editor):
Template = apps.get_model(my_app,Template)
Site = apps .get_model(my_app,Site)

accept_reject_template = Template.objects.get(name =Accept / Reject)
Site.objects.filter(template = None)。 update(template = accept_reject_template)

class Migration(migrations.Migration):

依赖关系= [
('my_app','0021_auto_20150210_1008'),


operations = [
migrations.RunPython(add_default_template),
migrations.AlterField(
model_name ='site',
name ='template ',
field = models.ForeignKey(to ='my_app.Template'),
preserve_default = False,
),
]

如果我正确理解,当一个字段被更改为不可空时,可能会发生此错误,但是字段包含空值。
在这种情况下,我可以想到为什么会发生这种情况的唯一原因是因为 RunPython 操作事务在运行之前没有提交数据库中的更改



如果这确实是原因 - 如何确保更改在数据库中反映?
如果没有 - 可能是错误的原因?



谢谢!

解决方案

这是因为Django创建约束作为 DEFERRABLE INITIALLY DEFERRED

  ALTER TABLE my_app_site 
ADD CONSTRAINT[constraint_name]
FOREIGN KEY(template_id)
参考my_app_template(id)
DEFERRABLE初始延期

这告诉PostgreSQL,外键不需要在每个命令之后被检查,但可以是推迟到交易结束。



因此,当事务修改内容和结构时,约束将与结构更改并行检查,或者检查计划为在改变结构后完成。这两个状态都不好,数据库将中止事务,而不是作出任何假设。



您可以指示PostgreSQL在当前事务中立即检查约束通过调用 SET CONSTRAINTS ALL IMMEDIATE ,因此结构更改不会是一个问题(请参阅 SET CONSTRAINTS 文档)。您的迁移应如下所示:

  operations = [
migrations.RunSQL('设置约束所有IMMEDIATE',
reverse_sql = migrations.RunSQL.noop),

#...这里的实际迁移操作...

migrations.RunSQL (migrations.RunSQL.noop,
reverse_sql ='SET CONSTRAINTS ALL IMMEDIATE'),
]

第一个操作是应用(转发)迁移,最后一个操作是用于取消应用(向后)迁移。



编辑: strong>约束延迟有助于避免插入排序,特别是对于具有循环依赖性的自引用表和表。所以要弯曲Django时要小心。


I want to alter a foreign key in one of my models that can currently have NULL values to not be nullable.

I removed the null=True from my field and ran makemigrations

Because I'm an altering a table that already has rows which contain NULL values in that field I am asked to provide a one-off value right away or edit the migration file and add a RunPython operation.

My RunPython operation is listed BEFORE the AlterField operation and does the required update for this field so it doesn't contain NULL values (only rows who already contain a NULL value).

But, the migration still fails with this error: django.db.utils.OperationalError: cannot ALTER TABLE "my_app_site" because it has pending trigger events

Here's my code:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

def add_default_template(apps, schema_editor):
    Template = apps.get_model("my_app", "Template")
    Site = apps.get_model("my_app", "Site")

    accept_reject_template = Template.objects.get(name="Accept/Reject")
    Site.objects.filter(template=None).update(template=accept_reject_template)    

class Migration(migrations.Migration):

    dependencies = [
        ('my_app', '0021_auto_20150210_1008'),
    ]

    operations = [
        migrations.RunPython(add_default_template),
        migrations.AlterField(
            model_name='site',
            name='template',
            field=models.ForeignKey(to='my_app.Template'),
            preserve_default=False,
        ),
    ]

If I understand correctly this error may occur when a field is altered to be not-nullable but the field contains null values. In that case, the only reason I can think of why this happens is because the RunPython operation transaction didn't "commit" the changes in the database before running the AlterField.

If this is indeed the reason - how can I make sure the changes reflect in the database? If not - what can be the reason for the error?

Thanks!

解决方案

This happens because Django creates constraints as DEFERRABLE INITIALLY DEFERRED:

ALTER TABLE my_app_site
ADD CONSTRAINT "[constraint_name]"
FOREIGN KEY (template_id)
REFERENCES my_app_template(id)
DEFERRABLE INITIALLY DEFERRED;

This tells PostgreSQL that the foreign key does not need to be checked right after every command, but can be deferred until the end of transactions.

So, when a transaction modifies content and structure, the constraints are checked on parallel with the structure changes, or the checks are scheduled to be done after altering the structure. Both of these states are bad and the database will abort the transaction instead of making any assumptions.

You can instruct PostgreSQL to check constraints immediately in the current transaction by calling SET CONSTRAINTS ALL IMMEDIATE, so structure changes won't be a problem (refer to SET CONSTRAINTS documentation). Your migration should look like this:

operations = [
    migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE',
                      reverse_sql=migrations.RunSQL.noop),

    # ... the actual migration operations here ...

    migrations.RunSQL(migrations.RunSQL.noop,
                      reverse_sql='SET CONSTRAINTS ALL IMMEDIATE'),
]

The first operation is for applying (forward) migrations, and the last one is for unapplying (backwards) migrations.

EDIT: Constraint deferring is useful to avoid insertion sorting, specially for self-referencing tables and tables with cyclic dependencies. So be careful when bending Django.

这篇关于Django使用RunPython进行迁移以提交更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆