summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/session.py19
-rw-r--r--lib/sqlalchemy/orm/state.py29
2 files changed, 39 insertions, 9 deletions
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index d5f418b1c..a3ec360d0 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -54,17 +54,14 @@ _sessions = weakref.WeakValueDictionary()
"""Weak-referencing dictionary of :class:`.Session` objects.
"""
+statelib._sessions = _sessions
+
def _state_session(state):
"""Given an :class:`.InstanceState`, return the :class:`.Session`
associated, if any.
"""
- if state.session_id:
- try:
- return _sessions[state.session_id]
- except KeyError:
- pass
- return None
+ return state.session
class _SessionClassMethods(object):
@@ -1273,7 +1270,13 @@ class Session(_SessionClassMethods):
)
def begin(self, subtransactions=False, nested=False, _subtrans=False):
"""Begin a transaction, or nested transaction,
- on this :class:`.Session`.
+ on this :class:`.Session`, if one is not already begun.
+
+ The :class:`_orm.Session` object features **autobegin** behavior,
+ so that normally it is not necessary to call the
+ :meth:`_orm.Session.begin`
+ method explicitly. However, it may be used in order to control
+ the scope of when the transactional state is begun.
When used to begin the outermost transaction, an error is raised
if this :class:`.Session` is already inside of a transaction.
@@ -1294,6 +1297,8 @@ class Session(_SessionClassMethods):
.. seealso::
+ :ref:`session_autobegin`
+
:ref:`unitofwork_transaction`
:meth:`.Session.begin_nested`
diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py
index cda98b890..08390328e 100644
--- a/lib/sqlalchemy/orm/state.py
+++ b/lib/sqlalchemy/orm/state.py
@@ -31,6 +31,10 @@ from .. import inspection
from .. import util
+# late-populated by session.py
+_sessions = None
+
+
@inspection._self_inspects
class InstanceState(interfaces.InspectionAttrInfo):
"""tracks state information at the instance level.
@@ -247,7 +251,6 @@ class InstanceState(interfaces.InspectionAttrInfo):
self._last_known_values[key] = NO_VALUE
@property
- @util.preload_module("sqlalchemy.orm.session")
def session(self):
"""Return the owning :class:`.Session` for this instance,
or ``None`` if none available.
@@ -260,7 +263,12 @@ class InstanceState(interfaces.InspectionAttrInfo):
fully detached under normal circumstances.
"""
- return util.preloaded.orm_session._state_session(self)
+ if self.session_id:
+ try:
+ return _sessions[self.session_id]
+ except KeyError:
+ pass
+ return None
@property
def object(self):
@@ -754,7 +762,10 @@ class InstanceState(interfaces.InspectionAttrInfo):
self.modified = True
instance_dict = self._instance_dict()
if instance_dict:
+ has_modified = bool(instance_dict._modified)
instance_dict._modified.add(self)
+ else:
+ has_modified = False
# only create _strong_obj link if attached
# to a session
@@ -763,6 +774,20 @@ class InstanceState(interfaces.InspectionAttrInfo):
if self.session_id:
self._strong_obj = inst
+ # if identity map already had modified objects,
+ # assume autobegin already occurred, else check
+ # for autobegin
+ if not has_modified:
+ # inline of autobegin, to ensure session transaction
+ # snapshot is established
+ try:
+ session = _sessions[self.session_id]
+ except KeyError:
+ pass
+ else:
+ if session._transaction is None:
+ session._autobegin()
+
if inst is None and attr:
raise orm_exc.ObjectDereferencedError(
"Can't emit change event for attribute '%s' - "