mirror of https://github.com/django/django.git
Refactored database exceptions wrapping.
Squashed commit of the following: commit 2181d833ed1a2e422494738dcef311164c4e097e Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Wed Feb 27 14:28:39 2013 +0100 Fixed #15901 -- Wrapped all PEP-249 exceptions. commit 5476a5d93c19aa2f928c497d39ce6e33f52694e2 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Feb 26 17:26:52 2013 +0100 Added PEP 3134 exception chaining. Thanks Jacob Kaplan-Moss for the suggestion. commit 9365fad0a650328002fb424457d675a273c95802 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Feb 26 17:13:49 2013 +0100 Improved API for wrapping database errors. Thanks Alex Gaynor for the proposal. commit 1b463b765f2826f73a8d9266795cd5da4f8d5e9e Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Feb 26 15:00:39 2013 +0100 Removed redundant exception wrapping. This is now taken care of by the cursor wrapper. commit 524bc7345a724bf526bdd2dd1bcf5ede67d6bb5c Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Feb 26 14:55:10 2013 +0100 Wrapped database exceptions in the base backend. This covers the most common PEP-249 APIs: - Connection APIs: close(), commit(), rollback(), cursor() - Cursor APIs: callproc(), close(), execute(), executemany(), fetchone(), fetchmany(), fetchall(), nextset(). Fixed #19920. commit a66746bb5f0839f35543222787fce3b6a0d0a3ea Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Feb 26 14:53:34 2013 +0100 Added a wrap_database_exception context manager and decorator. It re-throws backend-specific exceptions using Django's common wrappers.
This commit is contained in:
parent
50328f0a61
commit
59a3520875
|
@ -1,8 +1,11 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db.utils import (ConnectionHandler, ConnectionRouter,
|
from django.db.utils import (DEFAULT_DB_ALIAS,
|
||||||
load_backend, DEFAULT_DB_ALIAS, DatabaseError, IntegrityError)
|
DataError, OperationalError, IntegrityError, InternalError,
|
||||||
|
ProgrammingError, NotSupportedError, DatabaseError,
|
||||||
|
InterfaceError, Error,
|
||||||
|
load_backend, ConnectionHandler, ConnectionRouter)
|
||||||
|
|
||||||
__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
|
__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
|
||||||
'IntegrityError', 'DEFAULT_DB_ALIAS')
|
'IntegrityError', 'DEFAULT_DB_ALIAS')
|
||||||
|
|
|
@ -14,6 +14,7 @@ from django.db import DEFAULT_DB_ALIAS
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
from django.db.backends import util
|
from django.db.backends import util
|
||||||
from django.db.transaction import TransactionManagementError
|
from django.db.transaction import TransactionManagementError
|
||||||
|
from django.db.utils import DatabaseErrorWrapper
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -57,6 +58,9 @@ class BaseDatabaseWrapper(object):
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.alias)
|
return hash(self.alias)
|
||||||
|
|
||||||
|
def wrap_database_errors(self):
|
||||||
|
return DatabaseErrorWrapper(self.Database)
|
||||||
|
|
||||||
def get_connection_params(self):
|
def get_connection_params(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -70,20 +74,28 @@ class BaseDatabaseWrapper(object):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _cursor(self):
|
def _cursor(self):
|
||||||
if self.connection is None:
|
with self.wrap_database_errors():
|
||||||
conn_params = self.get_connection_params()
|
if self.connection is None:
|
||||||
self.connection = self.get_new_connection(conn_params)
|
conn_params = self.get_connection_params()
|
||||||
self.init_connection_state()
|
self.connection = self.get_new_connection(conn_params)
|
||||||
connection_created.send(sender=self.__class__, connection=self)
|
self.init_connection_state()
|
||||||
return self.create_cursor()
|
connection_created.send(sender=self.__class__, connection=self)
|
||||||
|
return self.create_cursor()
|
||||||
|
|
||||||
def _commit(self):
|
def _commit(self):
|
||||||
if self.connection is not None:
|
if self.connection is not None:
|
||||||
return self.connection.commit()
|
with self.wrap_database_errors():
|
||||||
|
return self.connection.commit()
|
||||||
|
|
||||||
def _rollback(self):
|
def _rollback(self):
|
||||||
if self.connection is not None:
|
if self.connection is not None:
|
||||||
return self.connection.rollback()
|
with self.wrap_database_errors():
|
||||||
|
return self.connection.rollback()
|
||||||
|
|
||||||
|
def _close(self):
|
||||||
|
if self.connection is not None:
|
||||||
|
with self.wrap_database_errors():
|
||||||
|
return self.connection.close()
|
||||||
|
|
||||||
def _enter_transaction_management(self, managed):
|
def _enter_transaction_management(self, managed):
|
||||||
"""
|
"""
|
||||||
|
@ -333,8 +345,9 @@ class BaseDatabaseWrapper(object):
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.validate_thread_sharing()
|
self.validate_thread_sharing()
|
||||||
if self.connection is not None:
|
try:
|
||||||
self.connection.close()
|
self._close()
|
||||||
|
finally:
|
||||||
self.connection = None
|
self.connection = None
|
||||||
self.set_clean()
|
self.set_clean()
|
||||||
|
|
||||||
|
|
|
@ -116,30 +116,22 @@ class CursorWrapper(object):
|
||||||
def execute(self, query, args=None):
|
def execute(self, query, args=None):
|
||||||
try:
|
try:
|
||||||
return self.cursor.execute(query, args)
|
return self.cursor.execute(query, args)
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.OperationalError as e:
|
except Database.OperationalError as e:
|
||||||
# Map some error codes to IntegrityError, since they seem to be
|
# Map some error codes to IntegrityError, since they seem to be
|
||||||
# misclassified and Django would prefer the more logical place.
|
# misclassified and Django would prefer the more logical place.
|
||||||
if e[0] in self.codes_for_integrityerror:
|
if e[0] in self.codes_for_integrityerror:
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
raise
|
||||||
except Database.DatabaseError as e:
|
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
||||||
def executemany(self, query, args):
|
def executemany(self, query, args):
|
||||||
try:
|
try:
|
||||||
return self.cursor.executemany(query, args)
|
return self.cursor.executemany(query, args)
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.OperationalError as e:
|
except Database.OperationalError as e:
|
||||||
# Map some error codes to IntegrityError, since they seem to be
|
# Map some error codes to IntegrityError, since they seem to be
|
||||||
# misclassified and Django would prefer the more logical place.
|
# misclassified and Django would prefer the more logical place.
|
||||||
if e[0] in self.codes_for_integrityerror:
|
if e[0] in self.codes_for_integrityerror:
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
raise
|
||||||
except Database.DatabaseError as e:
|
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr in self.__dict__:
|
if attr in self.__dict__:
|
||||||
|
@ -391,6 +383,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
'iendswith': 'LIKE %s',
|
'iendswith': 'LIKE %s',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Database = Database
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -501,6 +501,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
'iendswith': "LIKEC UPPER(%s) ESCAPE '\\'",
|
'iendswith': "LIKEC UPPER(%s) ESCAPE '\\'",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Database = Database
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -604,10 +606,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if self.connection is not None:
|
if self.connection is not None:
|
||||||
try:
|
try:
|
||||||
return self.connection.commit()
|
return self.connection.commit()
|
||||||
except Database.IntegrityError as e:
|
|
||||||
# In case cx_Oracle implements (now or in a future version)
|
|
||||||
# raising this specific exception
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
except Database.DatabaseError as e:
|
||||||
# cx_Oracle 5.0.4 raises a cx_Oracle.DatabaseError exception
|
# cx_Oracle 5.0.4 raises a cx_Oracle.DatabaseError exception
|
||||||
# with the following attributes and values:
|
# with the following attributes and values:
|
||||||
|
@ -620,7 +618,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if hasattr(x, 'code') and hasattr(x, 'message') \
|
if hasattr(x, 'code') and hasattr(x, 'message') \
|
||||||
and x.code == 2091 and 'ORA-02291' in x.message:
|
and x.code == 2091 and 'ORA-02291' in x.message:
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
raise
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def oracle_version(self):
|
def oracle_version(self):
|
||||||
|
@ -760,13 +758,11 @@ class FormatStylePlaceholderCursor(object):
|
||||||
self._guess_input_sizes([params])
|
self._guess_input_sizes([params])
|
||||||
try:
|
try:
|
||||||
return self.cursor.execute(query, self._param_generator(params))
|
return self.cursor.execute(query, self._param_generator(params))
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
except Database.DatabaseError as e:
|
||||||
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
||||||
if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
raise
|
||||||
|
|
||||||
def executemany(self, query, params=None):
|
def executemany(self, query, params=None):
|
||||||
# cx_Oracle doesn't support iterators, convert them to lists
|
# cx_Oracle doesn't support iterators, convert them to lists
|
||||||
|
@ -789,13 +785,11 @@ class FormatStylePlaceholderCursor(object):
|
||||||
try:
|
try:
|
||||||
return self.cursor.executemany(query,
|
return self.cursor.executemany(query,
|
||||||
[self._param_generator(p) for p in formatted])
|
[self._param_generator(p) for p in formatted])
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
except Database.DatabaseError as e:
|
||||||
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
||||||
if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
if hasattr(e.args[0], 'code') and e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
raise
|
||||||
|
|
||||||
def fetchone(self):
|
def fetchone(self):
|
||||||
row = self.cursor.fetchone()
|
row = self.cursor.fetchone()
|
||||||
|
|
|
@ -40,40 +40,6 @@ def utc_tzinfo_factory(offset):
|
||||||
raise AssertionError("database connection isn't set to UTC")
|
raise AssertionError("database connection isn't set to UTC")
|
||||||
return utc
|
return utc
|
||||||
|
|
||||||
class CursorWrapper(object):
|
|
||||||
"""
|
|
||||||
A thin wrapper around psycopg2's normal cursor class so that we can catch
|
|
||||||
particular exception instances and reraise them with the right types.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, cursor):
|
|
||||||
self.cursor = cursor
|
|
||||||
|
|
||||||
def execute(self, query, args=None):
|
|
||||||
try:
|
|
||||||
return self.cursor.execute(query, args)
|
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
||||||
def executemany(self, query, args):
|
|
||||||
try:
|
|
||||||
return self.cursor.executemany(query, args)
|
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
if attr in self.__dict__:
|
|
||||||
return self.__dict__[attr]
|
|
||||||
else:
|
|
||||||
return getattr(self.cursor, attr)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self.cursor)
|
|
||||||
|
|
||||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
needs_datetime_string_cast = False
|
needs_datetime_string_cast = False
|
||||||
can_return_id_from_insert = True
|
can_return_id_from_insert = True
|
||||||
|
@ -106,6 +72,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
'iendswith': 'LIKE UPPER(%s)',
|
'iendswith': 'LIKE UPPER(%s)',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Database = Database
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -207,7 +175,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
def create_cursor(self):
|
def create_cursor(self):
|
||||||
cursor = self.connection.cursor()
|
cursor = self.connection.cursor()
|
||||||
cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None
|
cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None
|
||||||
return CursorWrapper(cursor)
|
return cursor
|
||||||
|
|
||||||
def _enter_transaction_management(self, managed):
|
def _enter_transaction_management(self, managed):
|
||||||
"""
|
"""
|
||||||
|
@ -245,10 +213,3 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if ((self.transaction_state and self.transaction_state[-1]) or
|
if ((self.transaction_state and self.transaction_state[-1]) or
|
||||||
not self.features.uses_autocommit):
|
not self.features.uses_autocommit):
|
||||||
super(DatabaseWrapper, self).set_dirty()
|
super(DatabaseWrapper, self).set_dirty()
|
||||||
|
|
||||||
def _commit(self):
|
|
||||||
if self.connection is not None:
|
|
||||||
try:
|
|
||||||
return self.connection.commit()
|
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import warnings
|
import warnings
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
|
|
||||||
from django.db import utils
|
from django.db import utils
|
||||||
from django.db.backends import *
|
from django.db.backends import *
|
||||||
|
@ -291,6 +290,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
'iendswith': "LIKE %s ESCAPE '\\'",
|
'iendswith': "LIKE %s ESCAPE '\\'",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Database = Database
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -398,24 +399,14 @@ class SQLiteCursorWrapper(Database.Cursor):
|
||||||
"""
|
"""
|
||||||
def execute(self, query, params=()):
|
def execute(self, query, params=()):
|
||||||
query = self.convert_query(query)
|
query = self.convert_query(query)
|
||||||
try:
|
return Database.Cursor.execute(self, query, params)
|
||||||
return Database.Cursor.execute(self, query, params)
|
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
||||||
def executemany(self, query, param_list):
|
def executemany(self, query, param_list):
|
||||||
query = self.convert_query(query)
|
query = self.convert_query(query)
|
||||||
try:
|
return Database.Cursor.executemany(self, query, param_list)
|
||||||
return Database.Cursor.executemany(self, query, param_list)
|
|
||||||
except Database.IntegrityError as e:
|
|
||||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
except Database.DatabaseError as e:
|
|
||||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
|
||||||
|
|
||||||
def convert_query(self, query):
|
def convert_query(self, query):
|
||||||
return FORMAT_QMARK_REGEX.sub('?', query).replace('%%','%')
|
return FORMAT_QMARK_REGEX.sub('?', query).replace('%%', '%')
|
||||||
|
|
||||||
def _sqlite_date_extract(lookup_type, dt):
|
def _sqlite_date_extract(lookup_type, dt):
|
||||||
if dt is None:
|
if dt is None:
|
||||||
|
|
|
@ -22,7 +22,12 @@ class CursorWrapper(object):
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr in ('execute', 'executemany', 'callproc'):
|
if attr in ('execute', 'executemany', 'callproc'):
|
||||||
self.db.set_dirty()
|
self.db.set_dirty()
|
||||||
return getattr(self.cursor, attr)
|
cursor_attr = getattr(self.cursor, attr)
|
||||||
|
if attr in ('callproc', 'close', 'execute', 'executemany',
|
||||||
|
'fetchone', 'fetchmany', 'fetchall', 'nextset'):
|
||||||
|
return self.db.wrap_database_errors()(cursor_attr)
|
||||||
|
else:
|
||||||
|
return cursor_attr
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.cursor)
|
return iter(self.cursor)
|
||||||
|
@ -34,7 +39,8 @@ class CursorDebugWrapper(CursorWrapper):
|
||||||
self.db.set_dirty()
|
self.db.set_dirty()
|
||||||
start = time()
|
start = time()
|
||||||
try:
|
try:
|
||||||
return self.cursor.execute(sql, params)
|
with self.db.wrap_database_errors():
|
||||||
|
return self.cursor.execute(sql, params)
|
||||||
finally:
|
finally:
|
||||||
stop = time()
|
stop = time()
|
||||||
duration = stop - start
|
duration = stop - start
|
||||||
|
@ -51,7 +57,8 @@ class CursorDebugWrapper(CursorWrapper):
|
||||||
self.db.set_dirty()
|
self.db.set_dirty()
|
||||||
start = time()
|
start = time()
|
||||||
try:
|
try:
|
||||||
return self.cursor.executemany(sql, param_list)
|
with self.db.wrap_database_errors():
|
||||||
|
return self.cursor.executemany(sql, param_list)
|
||||||
finally:
|
finally:
|
||||||
stop = time()
|
stop = time()
|
||||||
duration = stop - start
|
duration = stop - start
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from functools import wraps
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
from threading import local
|
from threading import local
|
||||||
|
@ -12,16 +13,87 @@ from django.utils import six
|
||||||
|
|
||||||
DEFAULT_DB_ALIAS = 'default'
|
DEFAULT_DB_ALIAS = 'default'
|
||||||
|
|
||||||
# Define some exceptions that mirror the PEP249 interface.
|
|
||||||
# We will rethrow any backend-specific errors using these
|
class Error(StandardError):
|
||||||
# common wrappers
|
|
||||||
class DatabaseError(Exception):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DataError(DatabaseError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OperationalError(DatabaseError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IntegrityError(DatabaseError):
|
class IntegrityError(DatabaseError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InternalError(DatabaseError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ProgrammingError(DatabaseError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotSupportedError(DatabaseError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseErrorWrapper(object):
|
||||||
|
"""
|
||||||
|
Context manager and decorator that re-throws backend-specific database
|
||||||
|
exceptions using Django's common wrappers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, database):
|
||||||
|
"""
|
||||||
|
database is a module defining PEP-249 exceptions.
|
||||||
|
"""
|
||||||
|
self.database = database
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if exc_type is None:
|
||||||
|
return
|
||||||
|
for dj_exc_type in (
|
||||||
|
DataError,
|
||||||
|
OperationalError,
|
||||||
|
IntegrityError,
|
||||||
|
InternalError,
|
||||||
|
ProgrammingError,
|
||||||
|
NotSupportedError,
|
||||||
|
DatabaseError,
|
||||||
|
InterfaceError,
|
||||||
|
Error,
|
||||||
|
):
|
||||||
|
db_exc_type = getattr(self.database, dj_exc_type.__name__)
|
||||||
|
if issubclass(exc_type, db_exc_type):
|
||||||
|
dj_exc_value = dj_exc_type(*tuple(exc_value.args))
|
||||||
|
if six.PY3:
|
||||||
|
dj_exc_value.__cause__ = exc_value
|
||||||
|
six.reraise(dj_exc_type, dj_exc_value, traceback)
|
||||||
|
|
||||||
|
def __call__(self, func):
|
||||||
|
@wraps(func)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
with self:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def load_backend(backend_name):
|
def load_backend(backend_name):
|
||||||
# Look for a fully qualified database backend name
|
# Look for a fully qualified database backend name
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -119,18 +119,28 @@ NoReverseMatch
|
||||||
Database Exceptions
|
Database Exceptions
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Django wraps the standard database exceptions :exc:`DatabaseError` and
|
Django wraps the standard database exceptions so that your Django code has a
|
||||||
:exc:`IntegrityError` so that your Django code has a guaranteed common
|
guaranteed common implementation of these classes. These database exceptions
|
||||||
implementation of these classes. These database exceptions are
|
are provided in :mod:`django.db`.
|
||||||
provided in :mod:`django.db`.
|
|
||||||
|
|
||||||
|
.. exception:: Error
|
||||||
|
.. exception:: InterfaceError
|
||||||
.. exception:: DatabaseError
|
.. exception:: DatabaseError
|
||||||
|
.. exception:: DataError
|
||||||
|
.. exception:: OperationalError
|
||||||
.. exception:: IntegrityError
|
.. exception:: IntegrityError
|
||||||
|
.. exception:: InternalError
|
||||||
|
.. exception:: ProgrammingError
|
||||||
|
.. exception:: NotSupportedError
|
||||||
|
|
||||||
The Django wrappers for database exceptions behave exactly the same as
|
The Django wrappers for database exceptions behave exactly the same as
|
||||||
the underlying database exceptions. See :pep:`249`, the Python Database API
|
the underlying database exceptions. See :pep:`249`, the Python Database API
|
||||||
Specification v2.0, for further information.
|
Specification v2.0, for further information.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
Previous version of Django only wrapped ``DatabaseError`` and
|
||||||
|
``IntegrityError``.
|
||||||
|
|
||||||
.. exception:: models.ProtectedError
|
.. exception:: models.ProtectedError
|
||||||
|
|
||||||
Raised to prevent deletion of referenced objects when using
|
Raised to prevent deletion of referenced objects when using
|
||||||
|
|
|
@ -60,6 +60,8 @@ Minor features
|
||||||
* In addition to :lookup:`year`, :lookup:`month` and :lookup:`day`, the ORM
|
* In addition to :lookup:`year`, :lookup:`month` and :lookup:`day`, the ORM
|
||||||
now supports :lookup:`hour`, :lookup:`minute` and :lookup:`second` lookups.
|
now supports :lookup:`hour`, :lookup:`minute` and :lookup:`second` lookups.
|
||||||
|
|
||||||
|
* Django now wraps all PEP-249 exceptions.
|
||||||
|
|
||||||
* The default widgets for :class:`~django.forms.EmailField`,
|
* The default widgets for :class:`~django.forms.EmailField`,
|
||||||
:class:`~django.forms.URLField`, :class:`~django.forms.IntegerField`,
|
:class:`~django.forms.URLField`, :class:`~django.forms.IntegerField`,
|
||||||
:class:`~django.forms.FloatField` and :class:`~django.forms.DecimalField` use
|
:class:`~django.forms.FloatField` and :class:`~django.forms.DecimalField` use
|
||||||
|
|
Loading…
Reference in New Issue