2012-09-19 12 views
6

Ich habe diesen Code:IntegrityError: unterscheiden zwischen eindeutige Einschränkung und nicht null Verletzungen

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 
    ) 

Es wird versucht, einen Benutzer mit dem angegebenen ID und E-Mail zu erstellen, und wenn es bereits ein solches vorhanden ist - versucht, die bestehenden zu erhalten Aufzeichnung.

Ich weiß, das ist eine schlechte Konstruktion und es wird sowieso refaktoriert. Aber meine Frage ist:

Wie kann ich feststellen, welche Art von IntegrityError passiert: die eine im Zusammenhang mit unique Einschränkungsverletzung (es gibt eindeutige Schlüssel auf (user_id, E-Mail)) oder die zu not null Einschränkung verwendet (path nicht sei null)?

Antwort

6

psycopg2 bietet die SQLSTATE mit der Ausnahme als pgcode Mitglied, die Ihnen ziemlich feinkörnige Fehlerinformationen zum übereinstimmen gibt.

Siehe Appendix A: Error Codes in the PostgreSQL manual für Code Bedeutungen. Beachten Sie, dass Sie grob für die ersten beiden Zeichen für breite Kategorien übereinstimmen können. In diesem Fall kann ich sehen, dass SQLSTATE 42601 syntax_error in der Kategorie Syntax Error or Access Rule Violation ist.

Die Codes, die Sie wollen, sind:

23505 unique_violation 
23502 not_null_violation 

so könnten Sie schreiben:

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 

Das heißt, das ist eine schlechte Art und Weise ist eine upsert oder merge zu tun. @ pr0gg3d hat vermutlich recht, als er mit Django den richtigen Weg vorgeschlagen hat; Ich mache Django nicht, also kann ich nichts dazu sagen. Für allgemeine Informationen zum Upsert/Merge siehe depesz's article on the topic.

3

Es könnte besser sein, zu verwenden:

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

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

Die IntegrityError sollte nur im Fall gibt es eine NOT NULL Einschränkungsverletzung erhoben werden. Darüber hinaus können Sie created Flag verwenden, um zu wissen, ob das Objekt bereits vorhanden war.

+0

Wenn jemand das findet: get_or_create() ist sauberer aber immer noch nicht immer atomar und leidet unter Rennbedingungen (abhängig von Datenbankkonfiguration), wie in den Dokumenten erwähnt. – Bartvds

2

Ergänzung vom 2017.09.06:

Ein recht elegante Art und Weise, dies zu tun ist, um try/except IntegrityError as exc, und dann einige nützlichen Attribute verwenden, auf exc.__cause__ und exc.__cause__.diag (diagnostische Klasse, die Ihnen einige andere Super gibt relevante Informationen zum vorliegenden Fehler - Sie können es selbst mit dir(exc.__cause__.diag) erkunden.

Die erste, die Sie verwenden können, wurde oben beschrieben.Um Ihren Code zukunftssicher machen können Sie die psycopg2 Codes direkt verweisen, und Sie können sogar die Einschränkung überprüfen, die die Diagnose Klasse verletzt wurde unter Verwendung ich oben erwähnt:

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' 

bearbeiten zur Klärung: Ich habe verwenden, um die __cause__ Accessor, weil ich Django verwende, also um zur psycopg2 IntegrityError Klasse zu gelangen, muss ich exc.__cause__

anrufen
Verwandte Themen