diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-10-04 13:50:36 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-10-04 13:50:36 -0400 |
| commit | 1c3e3225521647cc843a633e34ed84e1ca4e797a (patch) | |
| tree | f844e59c46b8df996c77e27f65dd6840ce1e49ed /lib/sqlalchemy | |
| parent | f3e12698fbf03bc7c11a90f6d78d2b2a5efa70fd (diff) | |
| download | sqlalchemy-1c3e3225521647cc843a633e34ed84e1ca4e797a.tar.gz | |
- [feature] The Session will produce warnings
when unsupported methods are used inside the
"execute" portion of the flush. These are
the familiar methods add(), delete(), etc.
as well as collection and related-object
manipulations, as called within mapper-level
flush events
like after_insert(), after_update(), etc.
It's been prominently documented for a long
time that SQLAlchemy cannot guarantee
results when the Session is manipulated within
the execution of the flush plan,
however users are still doing it, so now
there's a warning. Maybe someday the Session
will be enhanced to support these operations
inside of the flush, but for now, results
can't be guaranteed.
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 30 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 15 |
2 files changed, 42 insertions, 3 deletions
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 1df9d45ca..dcbd6ba7e 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -555,6 +555,7 @@ class Session(_SessionClassMethods): self.bind = bind self.__binds = {} self._flushing = False + self._warn_on_events = False self.transaction = None self.hash_key = _new_sessionid() self.autoflush = autoflush @@ -1323,7 +1324,7 @@ class Session(_SessionClassMethods): self._deleted.pop(state, None) state.deleted = True - def add(self, instance): + def add(self, instance, _warn=True): """Place an object in the ``Session``. Its state will be persisted to the database on the next flush @@ -1333,6 +1334,9 @@ class Session(_SessionClassMethods): is ``expunge()``. """ + if _warn and self._warn_on_events: + self._flush_warning("Session.add()") + try: state = attributes.instance_state(instance) except exc.NO_STATE: @@ -1343,8 +1347,11 @@ class Session(_SessionClassMethods): def add_all(self, instances): """Add the given collection of instances to this ``Session``.""" + if self._warn_on_events: + self._flush_warning("Session.add_all()") + for instance in instances: - self.add(instance) + self.add(instance, _warn=False) def _save_or_update_state(self, state): self._save_or_update_impl(state) @@ -1362,6 +1369,9 @@ class Session(_SessionClassMethods): The database delete operation occurs upon ``flush()``. """ + if self._warn_on_events: + self._flush_warning("Session.delete()") + try: state = attributes.instance_state(instance) except exc.NO_STATE: @@ -1436,6 +1446,9 @@ class Session(_SessionClassMethods): """ + if self._warn_on_events: + self._flush_warning("Session.merge()") + _recursive = {} if load: @@ -1744,6 +1757,13 @@ class Session(_SessionClassMethods): finally: self._flushing = False + def _flush_warning(self, method): + util.warn("Usage of the '%s' operation is not currently supported " + "within the execution stage of the flush process. " + "Results may not be consistent. Consider using alternative " + "event listeners or connection-level operations instead." + % method) + def _is_clean(self): return not self.identity_map.check_modified() and \ not self._deleted and \ @@ -1811,7 +1831,11 @@ class Session(_SessionClassMethods): flush_context.transaction = transaction = self.begin( subtransactions=True) try: - flush_context.execute() + self._warn_on_events = True + try: + flush_context.execute() + finally: + self._warn_on_events = False self.dispatch.after_flush(self, flush_context) diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 5fb7a55e5..1cba58321 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -34,6 +34,9 @@ def track_cascade_events(descriptor, prop): sess = sessionlib._state_session(state) if sess: + if sess._warn_on_events: + sess._flush_warning("collection append") + prop = state.manager.mapper._props[key] item_state = attributes.instance_state(item) if prop.cascade.save_update and \ @@ -48,7 +51,15 @@ def track_cascade_events(descriptor, prop): sess = sessionlib._state_session(state) if sess: + prop = state.manager.mapper._props[key] + + if sess._warn_on_events: + sess._flush_warning( + "collection remove" + if prop.uselist + else "related attribute delete") + # expunge pending orphans item_state = attributes.instance_state(item) if prop.cascade.delete_orphan and \ @@ -64,6 +75,10 @@ def track_cascade_events(descriptor, prop): sess = sessionlib._state_session(state) if sess: + + if sess._warn_on_events: + sess._flush_warning("related attribute set") + prop = state.manager.mapper._props[key] if newvalue is not None: newvalue_state = attributes.instance_state(newvalue) |
