Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python IntegrityError:区分唯一约束和非空冲突_Python_Django_Postgresql_Psycopg2 - Fatal编程技术网

Python IntegrityError:区分唯一约束和非空冲突

Python IntegrityError:区分唯一约束和非空冲突,python,django,postgresql,psycopg2,Python,Django,Postgresql,Psycopg2,我有以下代码: try: principal = cls.objects.create( user_id=user.id, email=user.email, path='something' ) except IntegrityError: principal = cls.objects.get( user_id=user.id, email=user.email ) 它尝试创建具

我有以下代码:

try:
    principal = cls.objects.create(
        user_id=user.id,
        email=user.email,
        path='something'
    )
except IntegrityError:
    principal = cls.objects.get(
        user_id=user.id,
        email=user.email
    )
它尝试创建具有给定id和电子邮件的用户,如果已经存在,则尝试获取现有记录

我知道这是一个糟糕的构造,无论如何它都会被重构。但我的问题是:


我如何确定发生了什么类型的
IntegrityError
:与
unique
约束冲突相关的错误(存在唯一密钥(用户id,电子邮件))或与
not null
约束相关的错误(
路径
不能为null)?

最好使用:

try:
    obj, created = cls.objects.get_or_create(user_id=user.id, email=user.email)
except IntegrityError:
    ....

只有在存在
notnull
约束冲突的情况下,才应引发
IntegrityError

此外,您还可以使用
created
标志来了解对象是否已经存在。

psycopg2提供了
SQLSTATE
,但作为
pgcode
成员的除外,这为您提供了非常细粒度的错误信息以进行匹配

python3
>>> import psycopg2
>>> conn = psycopg2.connect("dbname=regress")
>>> curs = conn.cursor()
>>> try:
...     curs.execute("INVALID;")
... except Exception as ex:
...     xx = ex
>>> xx.pgcode
'42601'
有关代码含义,请参见。请注意,对于广泛的类别,您可以粗略地匹配前两个字符。在本例中,我可以看到SQLSTATE 42601在
语法错误或访问规则冲突
类别中是
语法错误

您需要的代码是:

23505   unique_violation
23502   not_null_violation
所以你可以写:

try:
    principal = cls.objects.create(
        user_id=user.id,
        email=user.email,
        path='something'
    )
except IntegrityError as ex:
    if ex.pgcode == '23505':
        principal = cls.objects.get(
            user_id=user.id,
            email=user.email
        )
    else:
        raise
这就是说,这是执行
upsert
merge
的糟糕方法@pr0gg3d在建议使用Django的正确方法方面可能是正确的;我不做Django,所以我不能对这一点发表评论。有关升级/合并的一般信息,请参见。

自2017年9月6日起的更新:

一个非常优雅的方法是
尝试
/
除了IntegrityError as exc
,然后在
exc.\uuuu cause.\uuuuu
exc.\uu cause.\uuuuuu.diag
上使用一些有用的属性(一个诊断类,可以为您提供有关当前错误的其他超级相关信息-您可以使用
dir(原因诊断除外)

您可以使用的第一个代码如上文所述。为了使您的代码更加经得起未来的考验,您可以直接引用psycopg2代码,甚至可以使用上面提到的诊断类检查违反的约束:

except IntegrityError as exc:
    from psycopg2 import errorcodes as pg_errorcodes
    assert exc.__cause__.pgcode == pg_errorcodes.UNIQUE_VIOLATION
    assert exc.__cause__.diag.constraint_name == 'tablename_colA_colB_unique_constraint'

编辑澄清:我必须使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
访问器,因为我使用的是Django,所以要访问PsycopgG2 IntegrityError类,我必须调用,如docs.IntegrityError中所述,针对多个错误进行了分析,包括NOTNULL和Unique约束失败。