繁体   English   中英

IntegrityError:区分唯一约束和非空违反

[英]IntegrityError: distinguish between unique constraint and not null violations

我有以下代码:

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约束违例有关(一种(在user_id,email上有唯一键))或一种与not null约束有关(一种path不能为null)?

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'

有关代码含义,请参见PostgreSQL手册中的附录A:错误代码 请注意,您可以在前两个字符上粗略地匹配广泛的类别。 在这种情况下,我可以看到“ SQLSTATE 42601”是“ Syntax Error or Access Rule Violation类别中的syntax_error

您想要的代码是:

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

就是说,这是进行upsertmerge的不好方法。 @ pr0gg3d大概是建议使用Django的正确方法的正确选择。 我不使用Django,所以我无法对此发表评论。 有关upsert / merge的一般信息,请参阅depesz关于该主题的文章

截至2017年9月6日更新:

一种非常优雅的方法是try / except IntegrityError as exc ,然后在exc.__cause__exc.__cause__ exc.__cause__.diag上使用一些有用的属性(诊断类为您提供了有关手头错误的其他一些超级相关信息-您可以使用dir(exc.__cause__.diag) )自己进行探索。

上面介绍了您可以使用的第一个。 为了使您的代码更适合将来使用,您可以直接引用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'

编辑说明:由于我使用的是Django,因此必须使用__cause__访问器,因此要进入psycopg2 IntegrityError类,必须调用exc.__cause__

最好使用:

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

https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create

仅在违反NOT NULL约束的情况下才应引发IntegrityError 此外,您可以使用created标志来确定对象是否已经存在。

暂无
暂无

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

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