简体   繁体   English

IntegrityError(Django ORM)中缺少表名

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

I am missing the table name in IntegrityError of Django: 我错过了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).

Is there a way to see which table the INSERT/UPDATE is accessing? 有没有办法查看INSERT / UPDATE正在访问哪个表?

We use PostgreSQL 9.6. 我们使用PostgreSQL 9.6。

This is a generic question : How to get a better error message? 这是一个通用问题 :如何获得更好的错误消息?

This is not a question about this particular column. 这不是关于这个特定专栏的问题。 I found the relevant table and column very soon. 我很快就找到了相关的表格和专栏。 But I want to improve the error message which is from our CI system. 但我想改进来自CI系统的错误消息。 The next time I want to see the table name immediately. 下次我想立即看到表名。

I know that I could reveal the missing information easily with a debugger if I see this error during software development. 我知道如果我在软件开发过程中看到此错误,我可以使用调试器轻松地显示缺少的信息。 But in my case this happens in production, and I have only the stacktrace like above. 但在我的情况下,这发生在生产中,我只有像上面这样的堆栈跟踪。

The exception message in this traceback is the original message from the database driver. 此回溯中的异常消息是来自数据库驱动程序的原始消息。 It is useful to know this and the traceback if anything is googled, reported etc. 知道这个和回溯是有用的,如果有任何谷歌搜索,报告等。

The exception class is the same django.db.utils. 异常类与django.db.utils相同。 IntegrityError for all backends, but the message or rather arguments depend on the backend: 所有后端的IntegrityError ,但消息或更确切的参数取决于后端:

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

The table name is visible only with sqlite3 backend. 表名仅在sqlite3后端可见。 Some backends use only a string argument of exception, but mysql uses two arguments: a numeric error code and a message. 一些后端仅使用异常的字符串参数,但mysql使用两个参数:数字错误代码和消息。 (I like to accept that it is a general question, not only PostgreSQL.) Authors of some backends expect that the author of the app will know the table name directly or from SQL, but it is not true with general ORM packages. (我喜欢接受这是一个普遍的问题,不仅仅是PostgreSQL。)一些后端的作者希望应用程序的作者直接或从SQL知道表名,但对于一般的ORM包不是这样。 There is no preferable and generally acceptable way, how to extend the message even if it can be done technically perfect. 没有优选且通常可接受的方式,即使可以在技术上完美地完成,如何扩展消息。

Development and debugging are easy: 开发和调试很简单:

  • Much additional information is available in DEBUG mode in development ("SQL" in the last frame or a class name of an object on a line like "myobj.save()") 开发中的DEBUG模式中提供了许多其他信息(最后一帧中的“SQL”或像“myobj.save()”这样的行上的对象的类名)
  • python manage.py test --debug-sql : "Prints logged SQL queries on failure." python manage.py test --debug-sql :“在失败时打印已记录的SQL查询。”
  • The same error in development/tests with sqlite3 is easier readable. 使用sqlite3进行开发/测试时出现的相同错误更易于阅读。

...but you are probably asking for a run-time error in production. ...但你可能要求在生产中出现运行时错误。

I guess about your possible intention in a so general question, what direction could be interesting for you. 我想在一个如此笼统的问题中你的可能意图,你可能会感兴趣的方向。

A) The most important information from the traceback is usually a few lines above the many lines with ".../django/db/..." . A) 回溯中最重要的信息通常是多行上面几行“... / django / db / ...” It is perfectly easy for a guru. 这对古茹来说非常容易。 It can be used very probably if the code is not so dynamic and general like a Django admin site, where no code near myobj.save() call (neither in parent frames) contains an explicit model name. 如果代码不像Django管理站点那样动态和通用,很可能使用它,其中myobj.save()调用附近没有代码(在父帧中都没有)包含显式模型名称。 Example: 例:

# 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) Catch the information by a common ancestor of your models 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

This could complicate debugging with multiple inheritance. 这可能会使多重继承的调试变得复杂。

C) An implementation in Django db would be better, but I can not imagine that it will be accepted and not reverted after some issue. C) Django数据库中实现会更好,但我无法想象它会被接受而不会在某些问题之后被还原。

if you can create sql function you can try: 如果你可以创建sql函数,你可以尝试:

create function to get last sequence value get_sequence_last_value ( original post ) 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;

after it get table with sequence more then in error stack, and has column manager_slug 在它获得带有序列的表之后,然后在错误堆栈中,并且具有列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';

i understand that the solution not full, but any way i hope it can help you 我明白解决方案并不完整,但我希望它可以帮助你

I would suggest to use Sentry ( https://sentry.io/welcome/ ). 我建议使用Sentry( https://sentry.io/welcome/ )。 In Sentry Issues you can observe all local variables for all parts of a stack trace. 在Sentry问题中,您可以观察堆栈跟踪的所有部分的所有局部变量。 哨兵问题的截图

The best solution that I have found to your problem is overwriting DataBaseErrorWrapper method, to do that go to \\django\\db\\utils.py and in the line 86 replace dj_exc_value = dj_exc_type(*exc_value.args) for: 我发现你的问题的最佳解决方案是覆盖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)

and if the IntegrityError appears, the message should work 如果出现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'

I am not sure but this should work with these exceptions: 我不确定,但这应该适用于以下例外情况:

DataError
OperationalError
IntegrityError
InternalError
ProgrammingError
NotSupportedError

It works for me, tell me if works to you. 它适用于我,告诉我是否适合你。 Remember edit the file with the Django folder that you are working 请记住使用您正在使用的Django文件夹编辑该文件

You miss set value at column "manager_slug". 您在“manager_slug”列中错过了设置值。 You cant set NULL value at this column. 您无法在此列设置NULL值。 You should set value or remove not-null condition. 您应该设置值或删除非空条件。

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

Is there a way to see which table the INSERT/UPDATE is accessing? 有没有办法查看INSERT / UPDATE正在访问哪个表?

If you're running migrate , you could just visit up the stdout and check which migration is being executed. 如果您正在运行migrate ,则可以访问stdout并检查正在执行的迁移。 Then you can open the migration file and have a better overview of the issue. 然后,您可以打开迁移文件并更好地概述该问题。

If you don't i guess you want to take a look at PEP249 for optional error handling. 如果你不这样我想你想看一下PEP249的可选错误处理。 as django database wrappper is based on PEP249 specification. 因为django数据库包装器基于PEP249规范。

References on django code within DatabaseErrorWrapper DatabaseErrorWrapper中引用django代码

2nd EDIT: 第二次编辑:

you could catch the integrity error and access the .messages attribute from the database wrapper. 您可以捕获完整性错误并从数据库包装器访问.messages属性。

pseudo example: 伪示例:

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

I tend to use as less dependence as possible and leave 3rd party libs as they are. 我倾向于使用尽可能少的依赖性并保留第三方库。

Most of the time, i would log every sql insert/update/delete query. 大多数时候,我会记录每个sql插入/更新/删除查询。 In this way, i can identify which query went wrong easily without extra effort. 通过这种方式,我可以轻松识别哪个查询出错,而无需额外的努力。 This allows me to track who did what at when in my system. 这使我可以跟踪谁在我的系统中做了什么。

That happens to meet part of regulatory requirement on actions tracking in my industry. 这恰好符合我所在行业的行动跟踪监管要求的一部分。

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

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