]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- The :class:`.exc.StatementError` or DBAPI-related subclass
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 11 Dec 2013 19:30:18 +0000 (14:30 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 11 Dec 2013 19:30:18 +0000 (14:30 -0500)
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.

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/exc.py
lib/sqlalchemy/orm/session.py

index e658e6928fd6d0f41f6bba637becd95cacbe6b49..fa1006694d7b2a768ea840d40921afa5301f5cae 100644 (file)
 .. 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
index 3c0c6c3657141ca94b9f41d3a4826e0786eca06d..93722fe998cc4b979a27d1385d40e08ccfe8ac9b 100644 (file)
@@ -219,6 +219,10 @@ class StatementError(SQLAlchemyError):
         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,
@@ -227,8 +231,13 @@ class StatementError(SQLAlchemyError):
     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__()
index 553f6de972f2644e23a8b70143669196a8ea178a..b3720c94e27a95eb15584eea2f6b812bd984b8dd 100644 (file)
@@ -1180,7 +1180,18 @@ class Session(_SessionClassMethods):
 
     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.