.. changelog::
:version: 0.9.0b2
+ .. change::
+ :tags: feature, orm
+
+ The :class:`.exc.StatementError` or DBAPI-related subclass
+ now can accomodate additional information about the "reason" for
+ the exception; the :class:`.Session` now adds some detail to it
+ when the exception occurs within an autoflush. This approach
+ is taken as opposed to combining :class:`.FlushError` with
+ a Python 3 style "chained exception" approach so as to maintain
+ compatibility both with Py2K code as well as code that already
+ catches ``IntegrityError`` or similar.
+
.. change::
:tags: feature, postgresql
:pullreq: bitbucket:8
self.statement = statement
self.params = params
self.orig = orig
+ self.detail = []
+
+ def add_detail(self, msg):
+ self.detail.append(msg)
def __reduce__(self):
return self.__class__, (self.args[0], self.statement,
def __str__(self):
from sqlalchemy.sql import util
params_repr = util._repr_params(self.params, 10)
- return ' '.join((SQLAlchemyError.__str__(self),
- repr(self.statement), repr(params_repr)))
+
+ return ' '.join([
+ "(%s)" % det for det in self.detail
+ ] + [
+ SQLAlchemyError.__str__(self),
+ repr(self.statement), repr(params_repr)
+ ])
def __unicode__(self):
return self.__str__()
def _autoflush(self):
if self.autoflush and not self._flushing:
- self.flush()
+ try:
+ self.flush()
+ except sa_exc.StatementError as e:
+ # note we are reraising StatementError as opposed to
+ # raising FlushError with "chaining" to remain compatible
+ # with code that catches StatementError, IntegrityError,
+ # etc.
+ e.add_detail(
+ "raised as a result of Query-invoked autoflush; "
+ "consider using a session.no_autoflush block if this "
+ "flush is occuring prematurely")
+ util.raise_from_cause(e)
def refresh(self, instance, attribute_names=None, lockmode=None):
"""Expire and refresh the attributes on the given instance.