From deb7e76a42fac9995174f30baa365b4d03ddee3c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 25 Feb 2012 14:34:02 -0500 Subject: [PATCH] - [feature] Added "no_autoflush" context manager to Session, used with with: will temporarily disable autoflush. --- CHANGES | 4 ++++ lib/sqlalchemy/orm/session.py | 28 ++++++++++++++++++++++++++++ lib/sqlalchemy/util/__init__.py | 2 +- lib/sqlalchemy/util/compat.py | 6 ++++++ test/orm/test_session.py | 17 +++++++++++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d45f616a00..867ed01186 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,10 @@ CHANGES 0.7.6 ===== - orm + - [feature] Added "no_autoflush" context + manager to Session, used with with: + will temporarily disable autoflush. + - [bug] Fixed bug whereby MappedCollection would not get the appropriate collection instrumentation if it were only used diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index ba6ceaeeca..d01c1598ae 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -978,6 +978,34 @@ class Session(object): return self._query_cls(entities, self, **kwargs) + @property + @util.contextmanager + def no_autoflush(self): + """Return a context manager that disables autoflush. + + e.g.:: + + with session.no_autoflush: + + some_object = SomeClass() + session.add(some_object) + # won't autoflush + some_object.related_thing = session.query(SomeRelated).first() + + Operations that proceed within the ``with:`` block + will not be subject to flushes occurring upon query + access. This is useful when initializing a series + of objects which involve existing database queries, + where the uncompleted object should not yet be flushed. + + New in 0.7.6. + + """ + autoflush = self.autoflush + self.autoflush = False + yield self + self.autoflush = autoflush + def _autoflush(self): if self.autoflush and not self._flushing: self.flush() diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 5712940258..13914aa7dc 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -7,7 +7,7 @@ from compat import callable, cmp, reduce, defaultdict, py25_dict, \ threading, py3k_warning, jython, pypy, win32, set_types, buffer, pickle, \ update_wrapper, partial, md5_hex, decode_slice, dottedgetter,\ - parse_qsl, any + parse_qsl, any, contextmanager from _collections import NamedTuple, ImmutableContainer, immutabledict, \ Properties, OrderedProperties, ImmutableProperties, OrderedDict, \ diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index 07652f3c6c..99b92b1e34 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -56,6 +56,12 @@ def buffer(x): buffer = buffer # end Py2K +try: + from contextlib import contextmanager +except ImportError: + def contextmanager(fn): + return fn + try: from functools import update_wrapper except ImportError: diff --git a/test/orm/test_session.py b/test/orm/test_session.py index ac22455bdf..79852c7a4a 100644 --- a/test/orm/test_session.py +++ b/test/orm/test_session.py @@ -222,6 +222,23 @@ class SessionTest(_fixtures.FixtureTest): eq_(bind.connect().execute("select count(1) from users").scalar(), 1) sess.close() + @testing.requires.python26 + def test_with_no_autoflush(self): + User, users = self.classes.User, self.tables.users + + mapper(User, users) + sess = Session() + + u = User() + u.name = 'ed' + sess.add(u) + def go(obj): + assert u not in sess.query(User).all() + testing.run_as_contextmanager(sess.no_autoflush, go) + assert u in sess.new + assert u in sess.query(User).all() + assert u not in sess.new + def test_make_transient(self): users, User = self.tables.users, self.classes.User -- 2.47.2