15

私はこのコードを持っています:

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と電子メールでユーザーを作成しようとします。既に存在する場合は、既存のレコードを取得しようとします。

これは悪い構造であり、とにかくリファクタリングされることを私は知っています。しかし、私の質問はこれです:

制約違反に関連するもの((user_id、email)に一意のキーがある)または制約に関連するもの(nullにすることはできません)のどのような種類IntegrityErrorが発生したかを判断するにはどうすればよいですか?uniquenot nullpath

4

3 に答える 3

13

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:エラーコードを参照してください。幅広いカテゴリの最初の2つの文字を大まかに一致させることができることに注意してください。この場合、SQLSTATE42601がカテゴリに含まsyntax_errorれていることがわかりSyntax Error or Access Rule Violationます。

必要なコードは次のとおりです。

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をやっていないので、そのビットについてコメントすることはできません。アップサート/マージに関する一般的な情報については、トピックに関するdepeszの記事を参照してください。

于 2012-09-19T13:14:27.263 に答える
9

2017年9月6日現在の更新:

これを行うための非常に洗練された方法は、try/を実行しexcept IntegrityError as exc、次にいくつかの有用な属性を使用することです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'

明確にするために編集:__cause__Djangoを使用しているため、アクセサーを使用する必要があります。したがって、psycopg2IntegrityErrorクラスにアクセスするには呼び出す必要がありますexc.__cause__

于 2017-09-06T20:44:07.113 に答える
3

使用する方が良いかもしれません:

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のように

制約違反IntegrityErrorがある場合にのみ、を上げる必要があります。NOT NULLさらに、フラグを使用createdして、オブジェクトがすでに存在するかどうかを知ることができます。

于 2012-09-19T07:44:05.690 に答える