繁体   English   中英

IntegrityError(Django ORM)中缺少表名

[英]Missing table name in IntegrityError (Django ORM)

我错过了Django的IntegrityError中的表名:

Traceback (most recent call last):
...
    return self.cursor.execute(sql, params)
  File ".../django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File ".../django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
IntegrityError: null value in column "manager_slug" violates not-null constraint
DETAIL:  Failing row contains (17485, null, 2017-10-10 09:32:19, , 306).

有没有办法查看INSERT / UPDATE正在访问哪个表?

我们使用PostgreSQL 9.6。

这是一个通用问题 :如何获得更好的错误消息?

这不是关于这个特定专栏的问题。 我很快就找到了相关的表格和专栏。 但我想改进来自CI系统的错误消息。 下次我想立即看到表名。

我知道如果我在软件开发过程中看到此错误,我可以使用调试器轻松地显示缺少的信息。 但在我的情况下,这发生在生产中,我只有像上面这样的堆栈跟踪。

此回溯中的异常消息是来自数据库驱动程序的原始消息。 知道这个和回溯是有用的,如果有任何谷歌搜索,报告等。

异常类与django.db.utils相同。 所有后端的IntegrityError ,但消息或更确切的参数取决于后端:

  • postgresnull value in column "manager_slug" violates not-null constraint\\n DETAILS...\\n
  • mysql (1048, "Column 'manager_slug' cannot be null")
  • sqlite3 NOT NULL constraint failed: appname_modelname.manager_slug

表名仅在sqlite3后端可见。 一些后端仅使用异常的字符串参数,但mysql使用两个参数:数字错误代码和消息。 (我喜欢接受这是一个普遍的问题,不仅仅是PostgreSQL。)一些后端的作者希望应用程序的作者直接或从SQL知道表名,但对于一般的ORM包不是这样。 没有优选且通常可接受的方式,即使可以在技术上完美地完成,如何扩展消息。

开发和调试很简单:

  • 开发中的DEBUG模式中提供了许多其他信息(最后一帧中的“SQL”或像“myobj.save()”这样的行上的对象的类名)
  • python manage.py test --debug-sql :“在失败时打印已记录的SQL查询。”
  • 使用sqlite3进行开发/测试时出现的相同错误更易于阅读。

...但你可能要求在生产中出现运行时错误。

我想在一个如此笼统的问题中你的可能意图,你可能会感兴趣的方向。

A) 回溯中最重要的信息通常是多行上面几行“... / django / db / ...” 这对古茹来说非常容易。 如果代码不像Django管理站点那样动态和通用,很可能使用它,其中myobj.save()调用附近没有代码(在父帧中都没有)包含显式模型名称。 例:

# skip some initial universal code in ".../django/..."
...
# our apps start to be interesting... (maybe other installed app)
...
# START HERE: Open this line in the editor. If the function is universal, jump to the previous.
File ".../me/app/...py", line 47, in my...
  my_obj.save()
# skip many stack frames .../django/db/... below
File ".../django/db/models/base.py", line 734, in save
  # self.save_base(...    # this line 733 is not visible
      force_update=force_update, update_fields=update_fields)
...
# interesting only sql and params, but not visible in production
File ".../django/db/backends/utils.py", line 64, in execute
  return self.cursor.execute(sql, params)
IntegrityError (or DataError similarly)...

B)通过模型的共同祖先捕获信息

class ...(models.Model):
    def save(self, *args, **wkargs):
        try:
            super(..., self).save(*args, **wkargs)
        except django.db.utils.IntegrityError as exc:
            new_message = 'table {}'.format(self._meta.db_table)
            exc.extra_info = new_message
            # this is less compatible, but it doesn't require additional reading support
            # exc.args = exc.args + (new_message,)
            reraise

这可能会使多重继承的调试变得复杂。

C) Django数据库中实现会更好,但我无法想象它会被接受而不会在某些问题之后被还原。

如果你可以创建sql函数,你可以尝试:

create function to get last sequence value get_sequence_last_value( original post

CREATE FUNCTION public.get_sequence_last_value(name) RETURNS int4 AS '
DECLARE
ls_sequence ALIAS FOR $1;
lr_record RECORD;
li_return INT4;
BEGIN
FOR lr_record IN EXECUTE ''SELECT last_value FROM '' || ls_sequence LOOP
li_return := lr_record.last_value;
END LOOP;
RETURN li_return;
END;' LANGUAGE 'plpgsql' VOLATILE;

在它获得带有序列的表之后,然后在错误堆栈中,并且具有列manager_slug

SELECT table_name, column_name 
FROM information_schema.columns 
WHERE table_name in (
    SELECT table_name

    FROM (
        SELECT table_name,
               get_sequence_last_value(
                    substr(column_default, 10, strpos(column_default, '::regclass') - 11)
                    ) as lv
        FROM information_schema.columns 
        WHERE column_default LIKE 'nextval%'
        ) as t_seq_lv

    WHERE lv > 17485
    )
   AND column_name = 'manager_slug';

我明白解决方案并不完整,但我希望它可以帮助你

我建议使用Sentry( https://sentry.io/welcome/ )。 在Sentry问题中,您可以观察堆栈跟踪的所有部分的所有局部变量。 哨兵问题的截图

我发现你的问题的最佳解决方案是覆盖DataBaseErrorWrapper方法,为了做到这一点,转到\\django\\db\\utils.py并在第86行替换dj_exc_value = dj_exc_type(*exc_value.args)

if exec_value.diag:
    a, b = exc_value.args + ("In the table '%s'" % exc_value.diag.table_name,)
    dj_exc_value = dj_exc_type(a + b)
else:
    dj_exc_value = dj_exc_type(*exc_value.args)

如果出现IntegrityError,则该消息应该有效

django.db.utils.IntegrityError: null value in column "signature" violates not-null constraint
DETAIL:  Failing row contains (89, null).
In the table 'teachers_signature'

我不确定,但这应该适用于以下例外情况:

DataError
OperationalError
IntegrityError
InternalError
ProgrammingError
NotSupportedError

它适用于我,告诉我是否适合你。 请记住使用您正在使用的Django文件夹编辑该文件

您在“manager_slug”列中错过了设置值。 您无法在此列设置NULL值。 您应该设置值或删除非空条件。

IntegrityError: null value in column "manager_slug" violates not-null constraint

有没有办法查看INSERT / UPDATE正在访问哪个表?

如果您正在运行migrate ,则可以访问stdout并检查正在执行的迁移。 然后,您可以打开迁移文件并更好地概述该问题。

如果你不这样我想你想看一下PEP249的可选错误处理。 因为django数据库包装器基于PEP249规范。

DatabaseErrorWrapper中引用django代码

第二次编辑:

您可以捕获完整性错误并从数据库包装器访问.messages属性。

伪示例:

try:
    # operation on database
except IntegrityError as ie:
    print(ie.wrapper.cursor.messages[:])

我倾向于使用尽可能少的依赖性并保留第三方库。

大多数时候,我会记录每个sql插入/更新/删除查询。 通过这种方式,我可以轻松识别哪个查询出错,而无需额外的努力。 这使我可以跟踪谁在我的系统中做了什么。

这恰好符合我所在行业的行动跟踪监管要求的一部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM