Ich habe viele Fehler mit der Nachricht erhalten:
"DatabaseError: current transaction is aborted, commands ignored until end of transaction block"
nach dem Wechsel von python-psycopg zu python-psycopg2 als Datenbank-Engine des Django-Projekts.
Der Code bleibt derselbe, weiß nur nicht, woher diese Fehler kommen.
Dies ist, was postgres tut, wenn eine Abfrage einen Fehler erzeugt und Sie versuchen, eine andere Abfrage auszuführen, ohne die Transaktion vorher rückgängig zu machen. (Sie können es als Sicherheitsfunktion betrachten, um Ihre Daten nicht zu beschädigen.)
Um dies zu beheben, sollten Sie herausfinden, wo im Code die fehlerhafte Abfrage ausgeführt wird. Es kann hilfreich sein, die Optionen log_statement und log_min_error_statement in Ihrem Postgresql-Server zu verwenden.
Um den Fehler zu beseitigen, rollen Sie die letzte (fehlerhafte) Transaktion zurück, nachdem Sie Ihren Code repariert haben:
from Django.db import transaction
transaction.rollback()
Sie können try-except verwenden, um das Auftreten des Fehlers zu verhindern:
from Django.db import transaction, DatabaseError
try:
a.save()
except DatabaseError:
transaction.rollback()
Siehe: Django-Dokumentation
Also bin ich auf dasselbe Thema gestoßen. Das Problem, das ich hier hatte, war, dass meine Datenbank nicht richtig synchronisiert wurde. Einfache Probleme scheinen immer die meisten Sorgen zu verursachen ...
Um Ihre Django-Datenbank in Ihrem App-Verzeichnis und im Terminal zu synchronisieren, geben Sie Folgendes ein:
$ python manage.py syncdb
Bearbeiten: Beachten Sie, dass das Problem mit dem Befehl "$ python manage.py migrate" gelöst werden kann, wenn Sie Django-south verwenden.
Viel Spaß beim Codieren!
Nach meiner Erfahrung passieren diese Fehler auf diese Weise:
try:
code_that_executes_bad_query()
# transaction on DB is now bad
except:
pass
# transaction on db is still bad
code_that_executes_working_query() # raises transaction error
An der zweiten Abfrage ist nichts falsch, aber da der echte Fehler abgefangen wurde, führt die zweite Abfrage zu dem (viel weniger informativen) Fehler.
edit: Dies geschieht nur, wenn die except
-Klausel IntegrityError
(oder eine andere einfache Datenbankausnahme) abfängt. Wenn Sie etwas wie DoesNotExist
abfangen, wird dieser Fehler nicht angezeigt, da DoesNotExist
die Transaktion nicht beschädigt.
Die Lektion hier ist nicht versuchen/außer/pass.
Ich denke, dass die priesterlichen Muster-Erwähnungen bei der Verwendung von PostgreSQL eher die übliche Ursache für dieses Problem sind.
Ich denke jedoch, dass es gültige Verwendungen für das Muster gibt, und ich denke nicht, dass dieses Problem ein Grund sein sollte, es immer zu vermeiden. Zum Beispiel:
try:
profile = user.get_profile()
except ObjectDoesNotExist:
profile = make_default_profile_for_user(user)
do_something_with_profile(profile)
Wenn Sie mit diesem Muster zufrieden sind, aber expliziten Code für die Transaktionsverarbeitung überall vermeiden möchten, sollten Sie den Autocommit-Modus (PostgreSQL 8.2+) aktivieren: https://docs.djangoproject.com/ de/dev/ref/database/# Autocommit-Modus
DATABASES['default'] = {
#.. you usual options...
'OPTIONS': {
'autocommit': True,
}
}
Ich bin nicht sicher, ob es wichtige Überlegungen zur Leistung gibt (oder von einer anderen Art).
In Flask musst du nur schreiben:
curs = conn.cursor()
curs.execute("ROLLBACK")
conn.commit()
P.S. Die Dokumentation geht hier https://www.postgresql.org/docs/9.4/static/sql-rollback.html
Wenn Sie dies in der interaktiven Shell erhalten und eine schnelle Lösung benötigen, führen Sie folgende Schritte aus:
from Django.db import connection
connection._rollback()
ursprünglich gesehen in diese Antwort
Ich habe ein ähnliches Verhalten festgestellt, als ich eine fehlgeschlagene Transaktion im Terminal postgres
ausgeführt habe. Danach wurde nichts durchlaufen, da sich database
im Status error
befindet. Allerdings nur als schnelle Lösung, wenn Sie es sich leisten können, rollback transaction
zu vermeiden. Folgendes hat den Trick für mich gemacht:
COMMIT;
Ich habe das Silimar-Problem. Die Lösung bestand darin, db zu migrieren (manage.py syncdb
oder manage.py schemamigration --auto <table name>
, wenn Sie Süd verwenden).
Was würden Sie tun, wenn Sie auf @priestc und @Sebastian so etwas tun?
try:
conn.commit()
except:
pass
cursor.execute( sql )
try:
return cursor.fetchall()
except:
conn.commit()
return None
Ich habe diesen Code gerade ausprobiert, und er scheint zu funktionieren, scheitert im Hintergrund, ohne sich um mögliche Fehler kümmern zu müssen, und funktioniert, wenn die Abfrage gut ist.
Ich hatte auch gerade diesen Fehler, aber es maskierte eine andere, relevantere Fehlermeldung, in der der Code versuchte, eine Zeichenfolge mit 125 Zeichen in einer Spalte mit 100 Zeichen zu speichern:
DatabaseError: value too long for type character varying(100)
Ich musste den Code debuggen, damit die obige Meldung angezeigt wird, andernfalls wird sie angezeigt
DatabaseError: current transaction is aborted
verwenden Sie einfach ein Rollback
Beispielcode
try:
cur.execute("CREATE TABLE IF NOT EXISTS test2 (id serial, qa text);")
except:
cur.execute("rollback")
cur.execute("CREATE TABLE IF NOT EXISTS test2 (id serial, qa text);")
Ich glaube, @ AnujGuptas Antwort ist richtig. Das Rollback kann jedoch selbst eine Ausnahme auslösen, die Sie abfangen und behandeln sollten:
from Django.db import transaction, DatabaseError
try:
a.save()
except DatabaseError:
try:
transaction.rollback()
except transaction.TransactionManagementError:
# Log or handle otherwise
Wenn Sie feststellen, dass Sie diesen Code an verschiedenen save()
-Speicherorten neu schreiben, können Sie die Methode extrahieren:
import traceback
def try_rolling_back():
try:
transaction.rollback()
log.warning('rolled back') # example handling
except transaction.TransactionManagementError:
log.exception(traceback.format_exc()) # example handling
Schließlich können Sie es mit einem Dekorationsprogramm verbessern, das Methoden schützt, die save()
verwenden:
from functools import wraps
def try_rolling_back_on_exception(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except:
traceback.print_exc()
try_rolling_back()
return wrapped
@try_rolling_back_on_exception
def some_saving_method():
# ...
model.save()
# ...
Selbst wenn Sie den Dekorator oben implementieren, ist es dennoch praktisch, try_rolling_back()
als extrahierte Methode beizubehalten, falls Sie ihn manuell verwenden müssen, wenn eine bestimmte Behandlung erforderlich ist und die generische Handhabung des Dekorators nicht ausreicht.
Dies ist ein sehr seltsames Verhalten für mich. Ich bin überrascht, dass niemand an Sicherheitspunkte dachte. In meinem Code fehlgeschlagene Abfrage war erwartetes Verhalten:
from Django.db import transaction
@transaction.commit_on_success
def update():
skipped = 0
for old_model in OldModel.objects.all():
try:
Model.objects.create(
group_id=old_model.group_uuid,
file_id=old_model.file_uuid,
)
except IntegrityError:
skipped += 1
return skipped
Ich habe den Code auf diese Weise geändert, um Sicherungspunkte zu verwenden:
from Django.db import transaction
@transaction.commit_on_success
def update():
skipped = 0
sid = transaction.savepoint()
for old_model in OldModel.objects.all():
try:
Model.objects.create(
group_id=old_model.group_uuid,
file_id=old_model.file_uuid,
)
except IntegrityError:
skipped += 1
transaction.savepoint_rollback(sid)
else:
transaction.savepoint_commit(sid)
return skipped
Ich bin auf dieses Problem gestoßen, der Fehler tritt auf, da die Fehlertransaktionen nicht ordnungsgemäß beendet wurden. Ich habe den Befehl postgresql_transactions
des Transaktionssteuerungsbefehls hier gefunden
Transaktionskontrolle
Die folgenden Befehle dienen zur Steuerung von Transaktionen
BEGIN TRANSACTION − To start a transaction.
COMMIT − To save the changes, alternatively you can use END TRANSACTION command.
ROLLBACK − To rollback the changes.
also benutze ich den END TRANSACTION
, um den Fehler TRANSACTION zu beenden, Code wie folgt:
for key_of_attribute, command in sql_command.items():
cursor = connection.cursor()
g_logger.info("execute command :%s" % (command))
try:
cursor.execute(command)
rows = cursor.fetchall()
g_logger.info("the command:%s result is :%s" % (command, rows))
result_list[key_of_attribute] = rows
g_logger.info("result_list is :%s" % (result_list))
except Exception as e:
cursor.execute('END TRANSACTION;')
g_logger.info("error command :%s and error is :%s" % (command, e))
return result_list
In Flask Shell brauchte ich nur eine session.rollback()
, um an dieser Stelle vorbeizukommen.