/lib/sqlalchemy/orm/session.py

https://github.com/biznixcn/sqlalchemy · Python · 2445 lines · 1997 code · 174 blank · 274 comment · 145 complexity · 13693bd16d5c462672f2bc6d693fae84 MD5 · raw file

  1. # orm/session.py
  2. # Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
  3. #
  4. # This module is part of SQLAlchemy and is released under
  5. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  6. """Provides the Session class and related utilities."""
  7. import weakref
  8. from .. import util, sql, engine, exc as sa_exc
  9. from ..sql import util as sql_util, expression
  10. from . import (
  11. SessionExtension, attributes, exc, query,
  12. loading, identity
  13. )
  14. from ..inspection import inspect
  15. from .base import (
  16. object_mapper, class_mapper,
  17. _class_to_mapper, _state_mapper, object_state,
  18. _none_set, state_str, instance_str
  19. )
  20. from .unitofwork import UOWTransaction
  21. from . import state as statelib
  22. import sys
  23. __all__ = ['Session', 'SessionTransaction', 'SessionExtension', 'sessionmaker']
  24. _sessions = weakref.WeakValueDictionary()
  25. """Weak-referencing dictionary of :class:`.Session` objects.
  26. """
  27. def _state_session(state):
  28. """Given an :class:`.InstanceState`, return the :class:`.Session`
  29. associated, if any.
  30. """
  31. if state.session_id:
  32. try:
  33. return _sessions[state.session_id]
  34. except KeyError:
  35. pass
  36. return None
  37. class _SessionClassMethods(object):
  38. """Class-level methods for :class:`.Session`, :class:`.sessionmaker`."""
  39. @classmethod
  40. def close_all(cls):
  41. """Close *all* sessions in memory."""
  42. for sess in _sessions.values():
  43. sess.close()
  44. @classmethod
  45. @util.dependencies("sqlalchemy.orm.util")
  46. def identity_key(cls, orm_util, *args, **kwargs):
  47. """Return an identity key.
  48. This is an alias of :func:`.util.identity_key`.
  49. """
  50. return orm_util.identity_key(*args, **kwargs)
  51. @classmethod
  52. def object_session(cls, instance):
  53. """Return the :class:`.Session` to which an object belongs.
  54. This is an alias of :func:`.object_session`.
  55. """
  56. return object_session(instance)
  57. ACTIVE = util.symbol('ACTIVE')
  58. PREPARED = util.symbol('PREPARED')
  59. COMMITTED = util.symbol('COMMITTED')
  60. DEACTIVE = util.symbol('DEACTIVE')
  61. CLOSED = util.symbol('CLOSED')
  62. class SessionTransaction(object):
  63. """A :class:`.Session`-level transaction.
  64. :class:`.SessionTransaction` is a mostly behind-the-scenes object
  65. not normally referenced directly by application code. It coordinates
  66. among multiple :class:`.Connection` objects, maintaining a database
  67. transaction for each one individually, committing or rolling them
  68. back all at once. It also provides optional two-phase commit behavior
  69. which can augment this coordination operation.
  70. The :attr:`.Session.transaction` attribute of :class:`.Session`
  71. refers to the current :class:`.SessionTransaction` object in use, if any.
  72. A :class:`.SessionTransaction` is associated with a :class:`.Session`
  73. in its default mode of ``autocommit=False`` immediately, associated
  74. with no database connections. As the :class:`.Session` is called upon
  75. to emit SQL on behalf of various :class:`.Engine` or :class:`.Connection`
  76. objects, a corresponding :class:`.Connection` and associated
  77. :class:`.Transaction` is added to a collection within the
  78. :class:`.SessionTransaction` object, becoming one of the
  79. connection/transaction pairs maintained by the
  80. :class:`.SessionTransaction`.
  81. The lifespan of the :class:`.SessionTransaction` ends when the
  82. :meth:`.Session.commit`, :meth:`.Session.rollback` or
  83. :meth:`.Session.close` methods are called. At this point, the
  84. :class:`.SessionTransaction` removes its association with its parent
  85. :class:`.Session`. A :class:`.Session` that is in ``autocommit=False``
  86. mode will create a new :class:`.SessionTransaction` to replace it
  87. immediately, whereas a :class:`.Session` that's in ``autocommit=True``
  88. mode will remain without a :class:`.SessionTransaction` until the
  89. :meth:`.Session.begin` method is called.
  90. Another detail of :class:`.SessionTransaction` behavior is that it is
  91. capable of "nesting". This means that the :meth:`.Session.begin` method
  92. can be called while an existing :class:`.SessionTransaction` is already
  93. present, producing a new :class:`.SessionTransaction` that temporarily
  94. replaces the parent :class:`.SessionTransaction`. When a
  95. :class:`.SessionTransaction` is produced as nested, it assigns itself to
  96. the :attr:`.Session.transaction` attribute. When it is ended via
  97. :meth:`.Session.commit` or :meth:`.Session.rollback`, it restores its
  98. parent :class:`.SessionTransaction` back onto the
  99. :attr:`.Session.transaction` attribute. The behavior is effectively a
  100. stack, where :attr:`.Session.transaction` refers to the current head of
  101. the stack.
  102. The purpose of this stack is to allow nesting of
  103. :meth:`.Session.rollback` or :meth:`.Session.commit` calls in context
  104. with various flavors of :meth:`.Session.begin`. This nesting behavior
  105. applies to when :meth:`.Session.begin_nested` is used to emit a
  106. SAVEPOINT transaction, and is also used to produce a so-called
  107. "subtransaction" which allows a block of code to use a
  108. begin/rollback/commit sequence regardless of whether or not its enclosing
  109. code block has begun a transaction. The :meth:`.flush` method, whether
  110. called explicitly or via autoflush, is the primary consumer of the
  111. "subtransaction" feature, in that it wishes to guarantee that it works
  112. within in a transaction block regardless of whether or not the
  113. :class:`.Session` is in transactional mode when the method is called.
  114. See also:
  115. :meth:`.Session.rollback`
  116. :meth:`.Session.commit`
  117. :meth:`.Session.begin`
  118. :meth:`.Session.begin_nested`
  119. :attr:`.Session.is_active`
  120. :meth:`.SessionEvents.after_commit`
  121. :meth:`.SessionEvents.after_rollback`
  122. :meth:`.SessionEvents.after_soft_rollback`
  123. """
  124. _rollback_exception = None
  125. def __init__(self, session, parent=None, nested=False):
  126. self.session = session
  127. self._connections = {}
  128. self._parent = parent
  129. self.nested = nested
  130. self._state = ACTIVE
  131. if not parent and nested:
  132. raise sa_exc.InvalidRequestError(
  133. "Can't start a SAVEPOINT transaction when no existing "
  134. "transaction is in progress")
  135. if self.session._enable_transaction_accounting:
  136. self._take_snapshot()
  137. if self.session.dispatch.after_transaction_create:
  138. self.session.dispatch.after_transaction_create(self.session, self)
  139. @property
  140. def is_active(self):
  141. return self.session is not None and self._state is ACTIVE
  142. def _assert_active(self, prepared_ok=False,
  143. rollback_ok=False,
  144. deactive_ok=False,
  145. closed_msg="This transaction is closed"):
  146. if self._state is COMMITTED:
  147. raise sa_exc.InvalidRequestError(
  148. "This session is in 'committed' state; no further "
  149. "SQL can be emitted within this transaction."
  150. )
  151. elif self._state is PREPARED:
  152. if not prepared_ok:
  153. raise sa_exc.InvalidRequestError(
  154. "This session is in 'prepared' state; no further "
  155. "SQL can be emitted within this transaction."
  156. )
  157. elif self._state is DEACTIVE:
  158. if not deactive_ok and not rollback_ok:
  159. if self._rollback_exception:
  160. raise sa_exc.InvalidRequestError(
  161. "This Session's transaction has been rolled back "
  162. "due to a previous exception during flush."
  163. " To begin a new transaction with this Session, "
  164. "first issue Session.rollback()."
  165. " Original exception was: %s"
  166. % self._rollback_exception
  167. )
  168. elif not deactive_ok:
  169. raise sa_exc.InvalidRequestError(
  170. "This Session's transaction has been rolled back "
  171. "by a nested rollback() call. To begin a new "
  172. "transaction, issue Session.rollback() first."
  173. )
  174. elif self._state is CLOSED:
  175. raise sa_exc.ResourceClosedError(closed_msg)
  176. @property
  177. def _is_transaction_boundary(self):
  178. return self.nested or not self._parent
  179. def connection(self, bindkey, **kwargs):
  180. self._assert_active()
  181. bind = self.session.get_bind(bindkey, **kwargs)
  182. return self._connection_for_bind(bind)
  183. def _begin(self, nested=False):
  184. self._assert_active()
  185. return SessionTransaction(
  186. self.session, self, nested=nested)
  187. def _iterate_parents(self, upto=None):
  188. if self._parent is upto:
  189. return (self,)
  190. else:
  191. if self._parent is None:
  192. raise sa_exc.InvalidRequestError(
  193. "Transaction %s is not on the active transaction list" % (
  194. upto))
  195. return (self,) + self._parent._iterate_parents(upto)
  196. def _take_snapshot(self):
  197. if not self._is_transaction_boundary:
  198. self._new = self._parent._new
  199. self._deleted = self._parent._deleted
  200. self._dirty = self._parent._dirty
  201. self._key_switches = self._parent._key_switches
  202. return
  203. if not self.session._flushing:
  204. self.session.flush()
  205. self._new = weakref.WeakKeyDictionary()
  206. self._deleted = weakref.WeakKeyDictionary()
  207. self._dirty = weakref.WeakKeyDictionary()
  208. self._key_switches = weakref.WeakKeyDictionary()
  209. def _restore_snapshot(self, dirty_only=False):
  210. assert self._is_transaction_boundary
  211. for s in set(self._new).union(self.session._new):
  212. self.session._expunge_state(s)
  213. if s.key:
  214. del s.key
  215. for s, (oldkey, newkey) in self._key_switches.items():
  216. self.session.identity_map.discard(s)
  217. s.key = oldkey
  218. self.session.identity_map.replace(s)
  219. for s in set(self._deleted).union(self.session._deleted):
  220. if s.deleted:
  221. #assert s in self._deleted
  222. del s.deleted
  223. self.session._update_impl(s, discard_existing=True)
  224. assert not self.session._deleted
  225. for s in self.session.identity_map.all_states():
  226. if not dirty_only or s.modified or s in self._dirty:
  227. s._expire(s.dict, self.session.identity_map._modified)
  228. def _remove_snapshot(self):
  229. assert self._is_transaction_boundary
  230. if not self.nested and self.session.expire_on_commit:
  231. for s in self.session.identity_map.all_states():
  232. s._expire(s.dict, self.session.identity_map._modified)
  233. for s in self._deleted:
  234. s.session_id = None
  235. self._deleted.clear()
  236. def _connection_for_bind(self, bind):
  237. self._assert_active()
  238. if bind in self._connections:
  239. return self._connections[bind][0]
  240. if self._parent:
  241. conn = self._parent._connection_for_bind(bind)
  242. if not self.nested:
  243. return conn
  244. else:
  245. if isinstance(bind, engine.Connection):
  246. conn = bind
  247. if conn.engine in self._connections:
  248. raise sa_exc.InvalidRequestError(
  249. "Session already has a Connection associated for the "
  250. "given Connection's Engine")
  251. else:
  252. conn = bind.contextual_connect()
  253. if self.session.twophase and self._parent is None:
  254. transaction = conn.begin_twophase()
  255. elif self.nested:
  256. transaction = conn.begin_nested()
  257. else:
  258. transaction = conn.begin()
  259. self._connections[conn] = self._connections[conn.engine] = \
  260. (conn, transaction, conn is not bind)
  261. self.session.dispatch.after_begin(self.session, self, conn)
  262. return conn
  263. def prepare(self):
  264. if self._parent is not None or not self.session.twophase:
  265. raise sa_exc.InvalidRequestError(
  266. "'twophase' mode not enabled, or not root transaction; "
  267. "can't prepare.")
  268. self._prepare_impl()
  269. def _prepare_impl(self):
  270. self._assert_active()
  271. if self._parent is None or self.nested:
  272. self.session.dispatch.before_commit(self.session)
  273. stx = self.session.transaction
  274. if stx is not self:
  275. for subtransaction in stx._iterate_parents(upto=self):
  276. subtransaction.commit()
  277. if not self.session._flushing:
  278. for _flush_guard in range(100):
  279. if self.session._is_clean():
  280. break
  281. self.session.flush()
  282. else:
  283. raise exc.FlushError(
  284. "Over 100 subsequent flushes have occurred within "
  285. "session.commit() - is an after_flush() hook "
  286. "creating new objects?")
  287. if self._parent is None and self.session.twophase:
  288. try:
  289. for t in set(self._connections.values()):
  290. t[1].prepare()
  291. except:
  292. with util.safe_reraise():
  293. self.rollback()
  294. self._state = PREPARED
  295. def commit(self):
  296. self._assert_active(prepared_ok=True)
  297. if self._state is not PREPARED:
  298. self._prepare_impl()
  299. if self._parent is None or self.nested:
  300. for t in set(self._connections.values()):
  301. t[1].commit()
  302. self._state = COMMITTED
  303. self.session.dispatch.after_commit(self.session)
  304. if self.session._enable_transaction_accounting:
  305. self._remove_snapshot()
  306. self.close()
  307. return self._parent
  308. def rollback(self, _capture_exception=False):
  309. self._assert_active(prepared_ok=True, rollback_ok=True)
  310. stx = self.session.transaction
  311. if stx is not self:
  312. for subtransaction in stx._iterate_parents(upto=self):
  313. subtransaction.close()
  314. if self._state in (ACTIVE, PREPARED):
  315. for transaction in self._iterate_parents():
  316. if transaction._parent is None or transaction.nested:
  317. transaction._rollback_impl()
  318. transaction._state = DEACTIVE
  319. break
  320. else:
  321. transaction._state = DEACTIVE
  322. sess = self.session
  323. if self.session._enable_transaction_accounting and \
  324. not sess._is_clean():
  325. # if items were added, deleted, or mutated
  326. # here, we need to re-restore the snapshot
  327. util.warn(
  328. "Session's state has been changed on "
  329. "a non-active transaction - this state "
  330. "will be discarded.")
  331. self._restore_snapshot(dirty_only=self.nested)
  332. self.close()
  333. if self._parent and _capture_exception:
  334. self._parent._rollback_exception = sys.exc_info()[1]
  335. sess.dispatch.after_soft_rollback(sess, self)
  336. return self._parent
  337. def _rollback_impl(self):
  338. for t in set(self._connections.values()):
  339. t[1].rollback()
  340. if self.session._enable_transaction_accounting:
  341. self._restore_snapshot(dirty_only=self.nested)
  342. self.session.dispatch.after_rollback(self.session)
  343. def close(self):
  344. self.session.transaction = self._parent
  345. if self._parent is None:
  346. for connection, transaction, autoclose in \
  347. set(self._connections.values()):
  348. if autoclose:
  349. connection.close()
  350. else:
  351. transaction.close()
  352. self._state = CLOSED
  353. if self.session.dispatch.after_transaction_end:
  354. self.session.dispatch.after_transaction_end(self.session, self)
  355. if self._parent is None:
  356. if not self.session.autocommit:
  357. self.session.begin()
  358. self.session = None
  359. self._connections = None
  360. def __enter__(self):
  361. return self
  362. def __exit__(self, type, value, traceback):
  363. self._assert_active(deactive_ok=True, prepared_ok=True)
  364. if self.session.transaction is None:
  365. return
  366. if type is None:
  367. try:
  368. self.commit()
  369. except:
  370. with util.safe_reraise():
  371. self.rollback()
  372. else:
  373. self.rollback()
  374. class Session(_SessionClassMethods):
  375. """Manages persistence operations for ORM-mapped objects.
  376. The Session's usage paradigm is described at :doc:`/orm/session`.
  377. """
  378. public_methods = (
  379. '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested',
  380. 'close', 'commit', 'connection', 'delete', 'execute', 'expire',
  381. 'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind',
  382. 'is_modified',
  383. 'merge', 'query', 'refresh', 'rollback',
  384. 'scalar')
  385. def __init__(self, bind=None, autoflush=True, expire_on_commit=True,
  386. _enable_transaction_accounting=True,
  387. autocommit=False, twophase=False,
  388. weak_identity_map=True, binds=None, extension=None,
  389. info=None,
  390. query_cls=query.Query):
  391. """Construct a new Session.
  392. See also the :class:`.sessionmaker` function which is used to
  393. generate a :class:`.Session`-producing callable with a given
  394. set of arguments.
  395. :param autocommit:
  396. .. warning::
  397. The autocommit flag is **not for general use**, and if it is used,
  398. queries should only be invoked within the span of a
  399. :meth:`.Session.begin` / :meth:`.Session.commit` pair. Executing
  400. queries outside of a demarcated transaction is a legacy mode
  401. of usage, and can in some cases lead to concurrent connection
  402. checkouts.
  403. Defaults to ``False``. When ``True``, the
  404. :class:`.Session` does not keep a persistent transaction running, and
  405. will acquire connections from the engine on an as-needed basis,
  406. returning them immediately after their use. Flushes will begin and
  407. commit (or possibly rollback) their own transaction if no
  408. transaction is present. When using this mode, the
  409. :meth:`.Session.begin` method is used to explicitly start
  410. transactions.
  411. .. seealso::
  412. :ref:`session_autocommit`
  413. :param autoflush: When ``True``, all query operations will issue a
  414. :meth:`~.Session.flush` call to this ``Session`` before proceeding.
  415. This is a convenience feature so that :meth:`~.Session.flush` need
  416. not be called repeatedly in order for database queries to retrieve
  417. results. It's typical that ``autoflush`` is used in conjunction with
  418. ``autocommit=False``. In this scenario, explicit calls to
  419. :meth:`~.Session.flush` are rarely needed; you usually only need to
  420. call :meth:`~.Session.commit` (which flushes) to finalize changes.
  421. :param bind: An optional :class:`.Engine` or :class:`.Connection` to
  422. which this ``Session`` should be bound. When specified, all SQL
  423. operations performed by this session will execute via this
  424. connectable.
  425. :param binds: An optional dictionary which contains more granular
  426. "bind" information than the ``bind`` parameter provides. This
  427. dictionary can map individual :class`.Table`
  428. instances as well as :class:`~.Mapper` instances to individual
  429. :class:`.Engine` or :class:`.Connection` objects. Operations which
  430. proceed relative to a particular :class:`.Mapper` will consult this
  431. dictionary for the direct :class:`.Mapper` instance as
  432. well as the mapper's ``mapped_table`` attribute in order to locate a
  433. connectable to use. The full resolution is described in the
  434. :meth:`.Session.get_bind`.
  435. Usage looks like::
  436. Session = sessionmaker(binds={
  437. SomeMappedClass: create_engine('postgresql://engine1'),
  438. somemapper: create_engine('postgresql://engine2'),
  439. some_table: create_engine('postgresql://engine3'),
  440. })
  441. Also see the :meth:`.Session.bind_mapper`
  442. and :meth:`.Session.bind_table` methods.
  443. :param \class_: Specify an alternate class other than
  444. ``sqlalchemy.orm.session.Session`` which should be used by the
  445. returned class. This is the only argument that is local to the
  446. :class:`.sessionmaker` function, and is not sent directly to the
  447. constructor for ``Session``.
  448. :param _enable_transaction_accounting: Defaults to ``True``. A
  449. legacy-only flag which when ``False`` disables *all* 0.5-style
  450. object accounting on transaction boundaries, including auto-expiry
  451. of instances on rollback and commit, maintenance of the "new" and
  452. "deleted" lists upon rollback, and autoflush of pending changes upon
  453. :meth:`~.Session.begin`, all of which are interdependent.
  454. :param expire_on_commit: Defaults to ``True``. When ``True``, all
  455. instances will be fully expired after each :meth:`~.commit`,
  456. so that all attribute/object access subsequent to a completed
  457. transaction will load from the most recent database state.
  458. :param extension: An optional
  459. :class:`~.SessionExtension` instance, or a list
  460. of such instances, which will receive pre- and post- commit and
  461. flush events, as well as a post-rollback event. **Deprecated.**
  462. Please see :class:`.SessionEvents`.
  463. :param info: optional dictionary of arbitrary data to be associated
  464. with this :class:`.Session`. Is available via the :attr:`.Session.info`
  465. attribute. Note the dictionary is copied at construction time so
  466. that modifications to the per-:class:`.Session` dictionary will be local
  467. to that :class:`.Session`.
  468. .. versionadded:: 0.9.0
  469. :param query_cls: Class which should be used to create new Query
  470. objects, as returned by the :meth:`~.Session.query` method. Defaults
  471. to :class:`.Query`.
  472. :param twophase: When ``True``, all transactions will be started as
  473. a "two phase" transaction, i.e. using the "two phase" semantics
  474. of the database in use along with an XID. During a
  475. :meth:`~.commit`, after :meth:`~.flush` has been issued for all
  476. attached databases, the :meth:`~.TwoPhaseTransaction.prepare` method
  477. on each database's :class:`.TwoPhaseTransaction` will be called.
  478. This allows each database to roll back the entire transaction,
  479. before each transaction is committed.
  480. :param weak_identity_map: Defaults to ``True`` - when set to
  481. ``False``, objects placed in the :class:`.Session` will be
  482. strongly referenced until explicitly removed or the
  483. :class:`.Session` is closed. **Deprecated** - this option
  484. is obsolete.
  485. """
  486. if weak_identity_map:
  487. self._identity_cls = identity.WeakInstanceDict
  488. else:
  489. util.warn_deprecated("weak_identity_map=False is deprecated. "
  490. "This feature is not needed.")
  491. self._identity_cls = identity.StrongInstanceDict
  492. self.identity_map = self._identity_cls()
  493. self._new = {} # InstanceState->object, strong refs object
  494. self._deleted = {} # same
  495. self.bind = bind
  496. self.__binds = {}
  497. self._flushing = False
  498. self._warn_on_events = False
  499. self.transaction = None
  500. self.hash_key = _new_sessionid()
  501. self.autoflush = autoflush
  502. self.autocommit = autocommit
  503. self.expire_on_commit = expire_on_commit
  504. self._enable_transaction_accounting = _enable_transaction_accounting
  505. self.twophase = twophase
  506. self._query_cls = query_cls
  507. if info:
  508. self.info.update(info)
  509. if extension:
  510. for ext in util.to_list(extension):
  511. SessionExtension._adapt_listener(self, ext)
  512. if binds is not None:
  513. for mapperortable, bind in binds.items():
  514. insp = inspect(mapperortable)
  515. if insp.is_selectable:
  516. self.bind_table(mapperortable, bind)
  517. elif insp.is_mapper:
  518. self.bind_mapper(mapperortable, bind)
  519. else:
  520. assert False
  521. if not self.autocommit:
  522. self.begin()
  523. _sessions[self.hash_key] = self
  524. connection_callable = None
  525. transaction = None
  526. """The current active or inactive :class:`.SessionTransaction`."""
  527. @util.memoized_property
  528. def info(self):
  529. """A user-modifiable dictionary.
  530. The initial value of this dictioanry can be populated using the
  531. ``info`` argument to the :class:`.Session` constructor or
  532. :class:`.sessionmaker` constructor or factory methods. The dictionary
  533. here is always local to this :class:`.Session` and can be modified
  534. independently of all other :class:`.Session` objects.
  535. .. versionadded:: 0.9.0
  536. """
  537. return {}
  538. def begin(self, subtransactions=False, nested=False):
  539. """Begin a transaction on this :class:`.Session`.
  540. If this Session is already within a transaction, either a plain
  541. transaction or nested transaction, an error is raised, unless
  542. ``subtransactions=True`` or ``nested=True`` is specified.
  543. The ``subtransactions=True`` flag indicates that this
  544. :meth:`~.Session.begin` can create a subtransaction if a transaction
  545. is already in progress. For documentation on subtransactions, please
  546. see :ref:`session_subtransactions`.
  547. The ``nested`` flag begins a SAVEPOINT transaction and is equivalent
  548. to calling :meth:`~.Session.begin_nested`. For documentation on
  549. SAVEPOINT transactions, please see :ref:`session_begin_nested`.
  550. """
  551. if self.transaction is not None:
  552. if subtransactions or nested:
  553. self.transaction = self.transaction._begin(
  554. nested=nested)
  555. else:
  556. raise sa_exc.InvalidRequestError(
  557. "A transaction is already begun. Use "
  558. "subtransactions=True to allow subtransactions.")
  559. else:
  560. self.transaction = SessionTransaction(
  561. self, nested=nested)
  562. return self.transaction # needed for __enter__/__exit__ hook
  563. def begin_nested(self):
  564. """Begin a `nested` transaction on this Session.
  565. The target database(s) must support SQL SAVEPOINTs or a
  566. SQLAlchemy-supported vendor implementation of the idea.
  567. For documentation on SAVEPOINT
  568. transactions, please see :ref:`session_begin_nested`.
  569. """
  570. return self.begin(nested=True)
  571. def rollback(self):
  572. """Rollback the current transaction in progress.
  573. If no transaction is in progress, this method is a pass-through.
  574. This method rolls back the current transaction or nested transaction
  575. regardless of subtransactions being in effect. All subtransactions up
  576. to the first real transaction are closed. Subtransactions occur when
  577. :meth:`.begin` is called multiple times.
  578. .. seealso::
  579. :ref:`session_rollback`
  580. """
  581. if self.transaction is None:
  582. pass
  583. else:
  584. self.transaction.rollback()
  585. def commit(self):
  586. """Flush pending changes and commit the current transaction.
  587. If no transaction is in progress, this method raises an
  588. :exc:`~sqlalchemy.exc.InvalidRequestError`.
  589. By default, the :class:`.Session` also expires all database
  590. loaded state on all ORM-managed attributes after transaction commit.
  591. This so that subsequent operations load the most recent
  592. data from the database. This behavior can be disabled using
  593. the ``expire_on_commit=False`` option to :class:`.sessionmaker` or
  594. the :class:`.Session` constructor.
  595. If a subtransaction is in effect (which occurs when begin() is called
  596. multiple times), the subtransaction will be closed, and the next call
  597. to ``commit()`` will operate on the enclosing transaction.
  598. When using the :class:`.Session` in its default mode of
  599. ``autocommit=False``, a new transaction will
  600. be begun immediately after the commit, but note that the newly begun
  601. transaction does *not* use any connection resources until the first
  602. SQL is actually emitted.
  603. .. seealso::
  604. :ref:`session_committing`
  605. """
  606. if self.transaction is None:
  607. if not self.autocommit:
  608. self.begin()
  609. else:
  610. raise sa_exc.InvalidRequestError("No transaction is begun.")
  611. self.transaction.commit()
  612. def prepare(self):
  613. """Prepare the current transaction in progress for two phase commit.
  614. If no transaction is in progress, this method raises an
  615. :exc:`~sqlalchemy.exc.InvalidRequestError`.
  616. Only root transactions of two phase sessions can be prepared. If the
  617. current transaction is not such, an
  618. :exc:`~sqlalchemy.exc.InvalidRequestError` is raised.
  619. """
  620. if self.transaction is None:
  621. if not self.autocommit:
  622. self.begin()
  623. else:
  624. raise sa_exc.InvalidRequestError("No transaction is begun.")
  625. self.transaction.prepare()
  626. def connection(self, mapper=None, clause=None,
  627. bind=None,
  628. close_with_result=False,
  629. **kw):
  630. """Return a :class:`.Connection` object corresponding to this
  631. :class:`.Session` object's transactional state.
  632. If this :class:`.Session` is configured with ``autocommit=False``,
  633. either the :class:`.Connection` corresponding to the current
  634. transaction is returned, or if no transaction is in progress, a new
  635. one is begun and the :class:`.Connection` returned (note that no
  636. transactional state is established with the DBAPI until the first
  637. SQL statement is emitted).
  638. Alternatively, if this :class:`.Session` is configured with
  639. ``autocommit=True``, an ad-hoc :class:`.Connection` is returned
  640. using :meth:`.Engine.contextual_connect` on the underlying
  641. :class:`.Engine`.
  642. Ambiguity in multi-bind or unbound :class:`.Session` objects can be
  643. resolved through any of the optional keyword arguments. This
  644. ultimately makes usage of the :meth:`.get_bind` method for resolution.
  645. :param bind:
  646. Optional :class:`.Engine` to be used as the bind. If
  647. this engine is already involved in an ongoing transaction,
  648. that connection will be used. This argument takes precedence
  649. over ``mapper``, ``clause``.
  650. :param mapper:
  651. Optional :func:`.mapper` mapped class, used to identify
  652. the appropriate bind. This argument takes precedence over
  653. ``clause``.
  654. :param clause:
  655. A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`,
  656. :func:`~.sql.expression.text`,
  657. etc.) which will be used to locate a bind, if a bind
  658. cannot otherwise be identified.
  659. :param close_with_result: Passed to :meth:`.Engine.connect`, indicating
  660. the :class:`.Connection` should be considered "single use",
  661. automatically closing when the first result set is closed. This
  662. flag only has an effect if this :class:`.Session` is configured with
  663. ``autocommit=True`` and does not already have a transaction
  664. in progress.
  665. :param \**kw:
  666. Additional keyword arguments are sent to :meth:`get_bind()`,
  667. allowing additional arguments to be passed to custom
  668. implementations of :meth:`get_bind`.
  669. """
  670. if bind is None:
  671. bind = self.get_bind(mapper, clause=clause, **kw)
  672. return self._connection_for_bind(bind,
  673. close_with_result=close_with_result)
  674. def _connection_for_bind(self, engine, **kwargs):
  675. if self.transaction is not None:
  676. return self.transaction._connection_for_bind(engine)
  677. else:
  678. return engine.contextual_connect(**kwargs)
  679. def execute(self, clause, params=None, mapper=None, bind=None, **kw):
  680. """Execute a SQL expression construct or string statement within
  681. the current transaction.
  682. Returns a :class:`.ResultProxy` representing
  683. results of the statement execution, in the same manner as that of an
  684. :class:`.Engine` or
  685. :class:`.Connection`.
  686. E.g.::
  687. result = session.execute(
  688. user_table.select().where(user_table.c.id == 5)
  689. )
  690. :meth:`~.Session.execute` accepts any executable clause construct, such
  691. as :func:`~.sql.expression.select`,
  692. :func:`~.sql.expression.insert`,
  693. :func:`~.sql.expression.update`,
  694. :func:`~.sql.expression.delete`, and
  695. :func:`~.sql.expression.text`. Plain SQL strings can be passed
  696. as well, which in the case of :meth:`.Session.execute` only
  697. will be interpreted the same as if it were passed via a
  698. :func:`~.expression.text` construct. That is, the following usage::
  699. result = session.execute(
  700. "SELECT * FROM user WHERE id=:param",
  701. {"param":5}
  702. )
  703. is equivalent to::
  704. from sqlalchemy import text
  705. result = session.execute(
  706. text("SELECT * FROM user WHERE id=:param"),
  707. {"param":5}
  708. )
  709. The second positional argument to :meth:`.Session.execute` is an
  710. optional parameter set. Similar to that of
  711. :meth:`.Connection.execute`, whether this is passed as a single
  712. dictionary, or a list of dictionaries, determines whether the DBAPI
  713. cursor's ``execute()`` or ``executemany()`` is used to execute the
  714. statement. An INSERT construct may be invoked for a single row::
  715. result = session.execute(users.insert(), {"id": 7, "name": "somename"})
  716. or for multiple rows::
  717. result = session.execute(users.insert(), [
  718. {"id": 7, "name": "somename7"},
  719. {"id": 8, "name": "somename8"},
  720. {"id": 9, "name": "somename9"}
  721. ])
  722. The statement is executed within the current transactional context of
  723. this :class:`.Session`. The :class:`.Connection` which is used
  724. to execute the statement can also be acquired directly by
  725. calling the :meth:`.Session.connection` method. Both methods use
  726. a rule-based resolution scheme in order to determine the
  727. :class:`.Connection`, which in the average case is derived directly
  728. from the "bind" of the :class:`.Session` itself, and in other cases
  729. can be based on the :func:`.mapper`
  730. and :class:`.Table` objects passed to the method; see the documentation
  731. for :meth:`.Session.get_bind` for a full description of this scheme.
  732. The :meth:`.Session.execute` method does *not* invoke autoflush.
  733. The :class:`.ResultProxy` returned by the :meth:`.Session.execute`
  734. method is returned with the "close_with_result" flag set to true;
  735. the significance of this flag is that if this :class:`.Session` is
  736. autocommitting and does not have a transaction-dedicated
  737. :class:`.Connection` available, a temporary :class:`.Connection` is
  738. established for the statement execution, which is closed (meaning,
  739. returned to the connection pool) when the :class:`.ResultProxy` has
  740. consumed all available data. This applies *only* when the
  741. :class:`.Session` is configured with autocommit=True and no
  742. transaction has been started.
  743. :param clause:
  744. An executable statement (i.e. an :class:`.Executable` expression
  745. such as :func:`.expression.select`) or string SQL statement
  746. to be executed.
  747. :param params:
  748. Optional dictionary, or list of dictionaries, containing
  749. bound parameter values. If a single dictionary, single-row
  750. execution occurs; if a list of dictionaries, an
  751. "executemany" will be invoked. The keys in each dictionary
  752. must correspond to parameter names present in the statement.
  753. :param mapper:
  754. Optional :func:`.mapper` or mapped class, used to identify
  755. the appropriate bind. This argument takes precedence over
  756. ``clause`` when locating a bind. See :meth:`.Session.get_bind`
  757. for more details.
  758. :param bind:
  759. Optional :class:`.Engine` to be used as the bind. If
  760. this engine is already involved in an ongoing transaction,
  761. that connection will be used. This argument takes
  762. precedence over ``mapper`` and ``clause`` when locating
  763. a bind.
  764. :param \**kw:
  765. Additional keyword arguments are sent to :meth:`.Session.get_bind()`
  766. to allow extensibility of "bind" schemes.
  767. .. seealso::
  768. :ref:`sqlexpression_toplevel` - Tutorial on using Core SQL
  769. constructs.
  770. :ref:`connections_toplevel` - Further information on direct
  771. statement execution.
  772. :meth:`.Connection.execute` - core level statement execution
  773. method, which is :meth:`.Session.execute` ultimately uses
  774. in order to execute the statement.
  775. """
  776. clause = expression._literal_as_text(clause)
  777. if bind is None:
  778. bind = self.get_bind(mapper, clause=clause, **kw)
  779. return self._connection_for_bind(bind, close_with_result=True).execute(
  780. clause, params or {})
  781. def scalar(self, clause, params=None, mapper=None, bind=None, **kw):
  782. """Like :meth:`~.Session.execute` but return a scalar result."""
  783. return self.execute(
  784. clause, params=params, mapper=mapper, bind=bind, **kw).scalar()
  785. def close(self):
  786. """Close this Session.
  787. This clears all items and ends any transaction in progress.
  788. If this session were created with ``autocommit=False``, a new
  789. transaction is immediately begun. Note that this new transaction does
  790. not use any connection resources until they are first needed.
  791. """
  792. self.expunge_all()
  793. if self.transaction is not None:
  794. for transaction in self.transaction._iterate_parents():
  795. transaction.close()
  796. def expunge_all(self):
  797. """Remove all object instances from this ``Session``.
  798. This is equivalent to calling ``expunge(obj)`` on all objects in this
  799. ``Session``.
  800. """
  801. for state in self.identity_map.all_states() + list(self._new):
  802. state._detach()
  803. self.identity_map = self._identity_cls()
  804. self._new = {}
  805. self._deleted = {}
  806. # TODO: need much more test coverage for bind_mapper() and similar !
  807. # TODO: + crystallize + document resolution order
  808. # vis. bind_mapper/bind_table
  809. def bind_mapper(self, mapper, bind):
  810. """Bind operations for a mapper to a Connectable.
  811. mapper
  812. A mapper instance or mapped class
  813. bind
  814. Any Connectable: a :class:`.Engine` or :class:`.Connection`.
  815. All subsequent operations involving this mapper will use the given
  816. `bind`.
  817. """
  818. if isinstance(mapper, type):
  819. mapper = class_mapper(mapper)
  820. self.__binds[mapper.base_mapper] = bind
  821. for t in mapper._all_tables:
  822. self.__binds[t] = bind
  823. def bind_table(self, table, bind):
  824. """Bind operations on a Table to a Connectable.
  825. table
  826. A :class:`.Table` instance
  827. bind
  828. Any Connectable: a :class:`.Engine` or :class:`.Connection`.
  829. All subsequent operations involving this :class:`.Table` will use the
  830. given `bind`.
  831. """
  832. self.__binds[table] = bind
  833. def get_bind(self, mapper=None, clause=None):
  834. """Return a "bind" to which this :class:`.Session` is bound.
  835. The "bind" is usually an instance of :class:`.Engine`,
  836. except in the case where the :class:`.Session` has been
  837. explicitly bound directly to a :class:`.Connection`.
  838. For a multiply-bound or unbound :class:`.Session`, the
  839. ``mapper`` or ``clause`` arguments are used to determine the
  840. appropriate bind to return.
  841. Note that the "mapper" argument is usually present
  842. when :meth:`.Session.get_bind` is called via an ORM
  843. operation such as a :meth:`.Session.query`, each
  844. individual INSERT/UPDATE/DELETE operation within a
  845. :meth:`.Session.flush`, call, etc.
  846. The order of resolution is:
  847. 1. if mapper given and session.binds is present,
  848. locate a bind based on mapper.
  849. 2. if clause given and session.binds is present,
  850. locate a bind based on :class:`.Table` objects
  851. found in the given clause present in session.binds.
  852. 3. if session.bind is present, return that.
  853. 4. if clause given, attempt to return a bind
  854. linked to the :class:`.MetaData` ultimately
  855. associated with the clause.
  856. 5. if mapper given, attempt to return a bind
  857. linked to the :class:`.MetaData` ultimately
  858. associated with the :class:`.Table` or other
  859. selectable to which the mapper is mapped.
  860. 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError`
  861. is raised.
  862. :param mapper:
  863. Optional :func:`.mapper` mapped class or instance of
  864. :class:`.Mapper`. The bind can be derived from a :class:`.Mapper`
  865. first by consulting the "binds" map associated with this
  866. :class:`.Session`, and secondly by consulting the :class:`.MetaData`
  867. associated with the :class:`.Table` to which the :class:`.Mapper`
  868. is mapped for a bind.
  869. :param clause:
  870. A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`,
  871. :func:`~.sql.expression.text`,
  872. etc.). If the ``mapper`` argument is not present or could not
  873. produce a bind, the given expression construct will be searched
  874. for a bound element, typically a :class:`.Table` associated with
  875. bound :class:`.MetaData`.
  876. """
  877. if mapper is clause is None:
  878. if self.bind:
  879. return self.bind
  880. else:
  881. raise sa_exc.UnboundExecutionError(
  882. "This session is not bound to a single Engine or "
  883. "Connection, and no context was provided to locate "
  884. "a binding.")
  885. c_mapper = mapper is not None and _class_to_mapper(mapper) or None
  886. # manually bound?
  887. if self.__binds:
  888. if c_mapper:
  889. if c_mapper.base_mapper in self.__binds:
  890. return self.__binds[c_mapper.base_mapper]
  891. elif c_mapper.mapped_table in self.__binds:
  892. return self.__binds[c_mapper.mapped_table]
  893. if clause is not None:
  894. for t in sql_util.find_tables(clause, include_crud=True):
  895. if t in self.__binds:
  896. return self.__binds[t]
  897. if self.bind:
  898. return self.bind
  899. if isinstance(clause, sql.expression.ClauseElement) and clause.bind:
  900. return clause.bind
  901. if c_mapper and c_mapper.mapped_table.bind:
  902. return c_mapper.mapped_table.bind
  903. context = []
  904. if mapper is not None:
  905. context.append('mapper %s' % c_mapper)
  906. if clause is not None:
  907. context.append('SQL expression')
  908. raise sa_exc.UnboundExecutionError(
  909. "Could not locate a bind configured on %s or this Session" % (
  910. ', '.join(context)))
  911. def query(self, *entities, **kwargs):
  912. """Return a new :class:`.Query` object corresponding to this
  913. :class:`.Session`."""
  914. return self._query_cls(entities, self, **kwargs)
  915. @property
  916. @util.contextmanager
  917. def no_autoflush(self):
  918. """Return a context manager that disables autoflush.
  919. e.g.::
  920. with session.no_autoflush:
  921. some_object = SomeClass()
  922. session.add(some_object)
  923. # won't autoflush
  924. some_object.related_thing = session.query(SomeRelated).first()
  925. Operations that proceed within the ``with:`` block
  926. will not be subject to flushes occurring upon query
  927. access. This is useful when initializing a series
  928. of objects which involve existing database queries,
  929. where the uncompleted object should not yet be flushed.
  930. .. versionadded:: 0.7.6
  931. """
  932. autoflush = self.autoflush
  933. self.autoflush = False
  934. yield self
  935. self.autoflush = autoflush
  936. def _autoflush(self):
  937. if self.autoflush and not self._flushing:
  938. try:
  939. self.flush()
  940. except sa_exc.StatementError as e:
  941. # note we are reraising StatementError as opposed to
  942. # raising FlushError with "chaining" to remain compatible
  943. # with code that catches StatementError, IntegrityError,
  944. # etc.
  945. e.add_detail(
  946. "raised as a result of Query-invoked autoflush; "
  947. "consider using a session.no_autoflush block if this "
  948. "flush is occurring prematurely")
  949. util.raise_from_cause(e)
  950. def refresh(self, instance, attribute_names=None, lockmode=None):
  951. """Expire and refresh the attributes on the given instance.
  952. A query will be issued to the database and all attributes will be
  953. refreshed with their current database value.
  954. Lazy-loaded relational attributes will remain lazily loaded, so that
  955. the instance-wide refresh operation will be followed immediately by
  956. the lazy load of that attribute.
  957. Eagerly-loaded relational attributes will eagerly load within the
  958. single refresh operation.
  959. Note that a highly isolated transaction will return the same values as
  960. were previously read in that same transaction, regardless of changes
  961. in database state outside of that transaction - usage of
  962. :meth:`~Session.refresh` usually only makes sense if non-ORM SQL
  963. statement were emitted in the ongoing transaction, or if autocommit
  964. mode is turned on.
  965. :param attribute_names: optional. An iterable collection of
  966. string attribute names indicating a subset of attributes to
  967. be refreshed.
  968. :param lockmode: Passed to the :class:`~sqlalchemy.orm.query.Query`
  969. as used by :meth:`~sqlalchemy.orm.query.Query.with_lockmode`.
  970. .. seealso::
  971. :ref:`session_expire` - introductory material
  972. :meth:`.Session.expire`
  973. :meth:`.Session.expire_all`
  974. """
  975. try:
  976. state = attributes.instance_state(instance)
  977. except exc.NO_STATE:
  978. raise exc.UnmappedInstanceError(instance)
  979. self._expire_state(state, attribute_names)
  980. if loading.load_on_ident(
  981. self.query(object_mapper(instance)),
  982. state.key, refresh_state=state,
  983. lockmode=lockmode,
  984. only_load_props=attribute_names) is None:
  985. raise sa_exc.InvalidRequestError(
  986. "Could not refresh instance '%s'" %
  987. instance_str(instance))
  988. def expire_all(self):
  989. """Expires all persistent instances within this Session.
  990. When any attributes on a persistent instance is next accessed,
  991. a query will be issued using the
  992. :class:`.Session` object's current transactional context in order to
  993. load all expired attributes for the given instance. Note that
  994. a highly isolated transaction will return the same values as were
  995. previously read in that same transaction, regardless of changes
  996. in database state outside of that transaction.
  997. To expire individual objects and individual attributes
  998. on those objects, use :meth:`Session.expire`.
  999. The :class:`.Session` object's default behavior is to
  1000. expire all state whenever the :meth:`Session.rollback`
  1001. or :meth:`Session.commit` methods are called, so that new
  1002. state can be loaded for the new transaction. For this reason,
  1003. calling :meth:`Session.expire_all` should not be needed when
  1004. autocommit is ``False``, assuming the transaction is isolated.
  1005. .. seealso::
  1006. :ref:`session_expire` - introductory material
  1007. :meth:`.Session.expire`
  1008. :meth:`.Session.refresh`
  1009. """
  1010. for state in self.identity_map.all_states():
  1011. state._expire(state.dict, self.identity_map._modified)
  1012. def expire(self, instance, attribute_names=None):
  1013. """Expire the attributes on an instance.
  1014. Marks the attributes of an instance as out of date. When an expired
  1015. attribute is next accessed, a query will be issued to the
  1016. :class:`.Session` object's current transactional context in order to
  1017. load all expired attributes for the given instance. Note that
  1018. a highly isolated transaction will return the same values as were
  1019. previously read in that same transaction, regardless of changes
  1020. in database state outside of that transaction.
  1021. To expire all objects in the :class:`.Session` simultaneously,
  1022. use :meth:`Session.expire_all`.
  1023. The :class:`.Session` object's default behavior is to
  1024. expire all state whenever the :meth:`Session.rollback`
  1025. or :meth:`Session.commit` methods are called, so that new
  1026. state can be loaded for the new transaction. For this reason,
  1027. calling :meth:`Session.expire` only makes sense for the specific
  1028. case that a non-ORM SQL statement was emitted in the current
  1029. transaction.
  1030. :param instance: The instance to be refreshed.
  1031. :param attribute_names: optional list of string attribute names
  1032. indicating a subset of attributes to be expired.
  1033. .. seealso::
  1034. :ref:`session_expire` - introductory material
  1035. :meth:`.Session.expire`
  1036. :meth:`.Session.refresh`
  1037. """
  1038. try:
  1039. state = attributes.instance_state(instance)
  1040. except exc.NO_STATE:
  1041. raise exc.UnmappedInstanceError(instance)
  1042. self._expire_state(state, attribute_names)
  1043. def _expire_state(self, state, attribute_names):
  1044. self._validate_persistent(state)
  1045. if attribute_names:
  1046. state._expire_attributes(state.dict, attribute_names)
  1047. else:
  1048. # pre-fetch the full cascade since the expire is going to
  1049. # remove associations
  1050. cascaded = list(state.manager.mapper.cascade_iterator(
  1051. 'refresh-expire', state))
  1052. self._conditional_expire(state)
  1053. for o, m, st_, dct_ in cascaded:
  1054. self._conditional_expire(st_)
  1055. def _conditional_expire(self, state):
  1056. """Expire a state if persistent, else expunge if pending"""
  1057. if state.key:
  1058. state._expire(state.dict, self.identity_map._modified)
  1059. elif state in self._new:
  1060. self._new.pop(state)
  1061. state._detach()
  1062. @util.deprecated("0.7", "The non-weak-referencing identity map "
  1063. "feature is no longer needed.")
  1064. def prune(self):
  1065. """Remove unreferenced instances cached in the identity map.
  1066. Note that this method is only meaningful if "weak_identity_map" is set
  1067. to False. The default weak identity map is self-pruning.
  1068. Removes any object in this Session's identity map that is not
  1069. referenced in user code, modified, new or scheduled for deletion.
  1070. Returns the number of objects pruned.
  1071. """
  1072. return self.identity_map.prune()
  1073. def expunge(self, instance):
  1074. """Remove the `instance` from this ``Session``.
  1075. This will free all internal references to the instance. Cascading
  1076. will be applied according to the *expunge* cascade rule.
  1077. """
  1078. try:
  1079. state = attributes.instance_state(instance)
  1080. except exc.NO_STATE:
  1081. raise exc.UnmappedInstanceError(instance)
  1082. if state.session_id is not self.hash_key:
  1083. raise sa_exc.InvalidRequestError(
  1084. "Instance %s is not present in this Session" %
  1085. state_str(state))
  1086. cascaded = list(state.manager.mapper.cascade_iterator(
  1087. 'expunge', state))
  1088. self._expunge_state(state)
  1089. for o, m, st_, dct_ in cascaded:
  1090. self._expunge_state(st_)
  1091. def _expunge_state(self, state):
  1092. if state in self._new:
  1093. self._new.pop(state)
  1094. state._detach()
  1095. elif self.identity_map.contains_state(state):
  1096. self.identity_map.discard(state)
  1097. self._deleted.pop(state, None)
  1098. state._detach()
  1099. elif self.transaction:
  1100. self.transaction._deleted.pop(state, None)
  1101. def _register_newly_persistent(self, states):
  1102. for state in states:
  1103. mapper = _state_mapper(state)
  1104. # prevent against last minute dereferences of the object
  1105. obj = state.obj()
  1106. if obj is not None:
  1107. instance_key = mapper._identity_key_from_state(state)
  1108. if _none_set.issubset(instance_key[1]) and \
  1109. not mapper.allow_partial_pks or \
  1110. _none_set.issuperset(instance_key[1]):
  1111. raise exc.FlushError(
  1112. "Instance %s has a NULL identity key. If this is an "
  1113. "auto-generated value, check that the database table "
  1114. "allows generation of new primary key values, and "
  1115. "that the mapped Column object is configured to "
  1116. "expect these generated values. Ensure also that "
  1117. "this flush() is not occurring at an inappropriate "
  1118. "time, such aswithin a load() event."
  1119. % state_str(state)
  1120. )
  1121. if state.key is None:
  1122. state.key = instance_key
  1123. elif state.key != instance_key:
  1124. # primary key switch. use discard() in case another
  1125. # state has already replaced this one in the identity
  1126. # map (see test/orm/test_naturalpks.py ReversePKsTest)
  1127. self.identity_map.discard(state)
  1128. if state in self.transaction._key_switches:
  1129. orig_key = self.transaction._key_switches[state][0]
  1130. else:
  1131. orig_key = state.key
  1132. self.transaction._key_switches[state] = (
  1133. orig_key, instance_key)
  1134. state.key = instance_key
  1135. self.identity_map.replace(state)
  1136. statelib.InstanceState._commit_all_states(
  1137. ((state, state.dict) for state in states),
  1138. self.identity_map
  1139. )
  1140. self._register_altered(states)
  1141. # remove from new last, might be the last strong ref
  1142. for state in set(states).intersection(self._new):
  1143. self._new.pop(state)
  1144. def _register_altered(self, states):
  1145. if self._enable_transaction_accounting and self.transaction:
  1146. for state in states:
  1147. if state in self._new:
  1148. self.transaction._new[state] = True
  1149. else:
  1150. self.transaction._dirty[state] = True
  1151. def _remove_newly_deleted(self, states):
  1152. for state in states:
  1153. if self._enable_transaction_accounting and self.transaction:
  1154. self.transaction._deleted[state] = True
  1155. self.identity_map.discard(state)
  1156. self._deleted.pop(state, None)
  1157. state.deleted = True
  1158. def add(self, instance, _warn=True):
  1159. """Place an object in the ``Session``.
  1160. Its state will be persisted to the database on the next flush
  1161. operation.
  1162. Repeated calls to ``add()`` will be ignored. The opposite of ``add()``
  1163. is ``expunge()``.
  1164. """
  1165. if _warn and self._warn_on_events:
  1166. self._flush_warning("Session.add()")
  1167. try:
  1168. state = attributes.instance_state(instance)
  1169. except exc.NO_STATE:
  1170. raise exc.UnmappedInstanceError(instance)
  1171. self._save_or_update_state(state)
  1172. def add_all(self, instances):
  1173. """Add the given collection of instances to this ``Session``."""
  1174. if self._warn_on_events:
  1175. self._flush_warning("Session.add_all()")
  1176. for instance in instances:
  1177. self.add(instance, _warn=False)
  1178. def _save_or_update_state(self, state):
  1179. self._save_or_update_impl(state)
  1180. mapper = _state_mapper(state)
  1181. for o, m, st_, dct_ in mapper.cascade_iterator(
  1182. 'save-update',
  1183. state,
  1184. halt_on=self._contains_state):
  1185. self._save_or_update_impl(st_)
  1186. def delete(self, instance):
  1187. """Mark an instance as deleted.
  1188. The database delete operation occurs upon ``flush()``.
  1189. """
  1190. if self._warn_on_events:
  1191. self._flush_warning("Session.delete()")
  1192. try:
  1193. state = attributes.instance_state(instance)
  1194. except exc.NO_STATE:
  1195. raise exc.UnmappedInstanceError(instance)
  1196. if state.key is None:
  1197. raise sa_exc.InvalidRequestError(
  1198. "Instance '%s' is not persisted" %
  1199. state_str(state))
  1200. if state in self._deleted:
  1201. return
  1202. # ensure object is attached to allow the
  1203. # cascade operation to load deferred attributes
  1204. # and collections
  1205. self._attach(state, include_before=True)
  1206. # grab the cascades before adding the item to the deleted list
  1207. # so that autoflush does not delete the item
  1208. # the strong reference to the instance itself is significant here
  1209. cascade_states = list(state.manager.mapper.cascade_iterator(
  1210. 'delete', state))
  1211. self._deleted[state] = state.obj()
  1212. self.identity_map.add(state)
  1213. for o, m, st_, dct_ in cascade_states:
  1214. self._delete_impl(st_)
  1215. def merge(self, instance, load=True):
  1216. """Copy the state of a given instance into a corresponding instance
  1217. within this :class:`.Session`.
  1218. :meth:`.Session.merge` examines the primary key attributes of the
  1219. source instance, and attempts to reconcile it with an instance of the
  1220. same primary key in the session. If not found locally, it attempts
  1221. to load the object from the database based on primary key, and if
  1222. none can be located, creates a new instance. The state of each
  1223. attribute on the source instance is then copied to the target instance.
  1224. The resulting target instance is then returned by the method; the
  1225. original source instance is left unmodified, and un-associated with the
  1226. :class:`.Session` if not already.
  1227. This operation cascades to associated instances if the association is
  1228. mapped with ``cascade="merge"``.
  1229. See :ref:`unitofwork_merging` for a detailed discussion of merging.
  1230. :param instance: Instance to be merged.
  1231. :param load: Boolean, when False, :meth:`.merge` switches into
  1232. a "high performance" mode which causes it to forego emitting history
  1233. events as well as all database access. This flag is used for
  1234. cases such as transferring graphs of objects into a :class:`.Session`
  1235. from a second level cache, or to transfer just-loaded objects
  1236. into the :class:`.Session` owned by a worker thread or process
  1237. without re-querying the database.
  1238. The ``load=False`` use case adds the caveat that the given
  1239. object has to be in a "clean" state, that is, has no pending changes
  1240. to be flushed - even if the incoming object is detached from any
  1241. :class:`.Session`. This is so that when
  1242. the merge operation populates local attributes and
  1243. cascades to related objects and
  1244. collections, the values can be "stamped" onto the
  1245. target object as is, without generating any history or attribute
  1246. events, and without the need to reconcile the incoming data with
  1247. any existing related objects or collections that might not
  1248. be loaded. The resulting objects from ``load=False`` are always
  1249. produced as "clean", so it is only appropriate that the given objects
  1250. should be "clean" as well, else this suggests a mis-use of the method.
  1251. """
  1252. if self._warn_on_events:
  1253. self._flush_warning("Session.merge()")
  1254. _recursive = {}
  1255. if load:
  1256. # flush current contents if we expect to load data
  1257. self._autoflush()
  1258. object_mapper(instance) # verify mapped
  1259. autoflush = self.autoflush
  1260. try:
  1261. self.autoflush = False
  1262. return self._merge(
  1263. attributes.instance_state(instance),
  1264. attributes.instance_dict(instance),
  1265. load=load, _recursive=_recursive)
  1266. finally:
  1267. self.autoflush = autoflush
  1268. def _merge(self, state, state_dict, load=True, _recursive=None):
  1269. mapper = _state_mapper(state)
  1270. if state in _recursive:
  1271. return _recursive[state]
  1272. new_instance = False
  1273. key = state.key
  1274. if key is None:
  1275. if not load:
  1276. raise sa_exc.InvalidRequestError(
  1277. "merge() with load=False option does not support "
  1278. "objects transient (i.e. unpersisted) objects. flush() "
  1279. "all changes on mapped instances before merging with "
  1280. "load=False.")
  1281. key = mapper._identity_key_from_state(state)
  1282. if key in self.identity_map:
  1283. merged = self.identity_map[key]
  1284. elif not load:
  1285. if state.modified:
  1286. raise sa_exc.InvalidRequestError(
  1287. "merge() with load=False option does not support "
  1288. "objects marked as 'dirty'. flush() all changes on "
  1289. "mapped instances before merging with load=False.")
  1290. merged = mapper.class_manager.new_instance()
  1291. merged_state = attributes.instance_state(merged)
  1292. merged_state.key = key
  1293. self._update_impl(merged_state)
  1294. new_instance = True
  1295. elif not _none_set.issubset(key[1]) or \
  1296. (mapper.allow_partial_pks and
  1297. not _none_set.issuperset(key[1])):
  1298. merged = self.query(mapper.class_).get(key[1])
  1299. else:
  1300. merged = None
  1301. if merged is None:
  1302. merged = mapper.class_manager.new_instance()
  1303. merged_state = attributes.instance_state(merged)
  1304. merged_dict = attributes.instance_dict(merged)
  1305. new_instance = True
  1306. self._save_or_update_state(merged_state)
  1307. else:
  1308. merged_state = attributes.instance_state(merged)
  1309. merged_dict = attributes.instance_dict(merged)
  1310. _recursive[state] = merged
  1311. # check that we didn't just pull the exact same
  1312. # state out.
  1313. if state is not merged_state:
  1314. # version check if applicable
  1315. if mapper.version_id_col is not None:
  1316. existing_version = mapper._get_state_attr_by_column(
  1317. state,
  1318. state_dict,
  1319. mapper.version_id_col,
  1320. passive=attributes.PASSIVE_NO_INITIALIZE)
  1321. merged_version = mapper._get_state_attr_by_column(
  1322. merged_state,
  1323. merged_dict,
  1324. mapper.version_id_col,
  1325. passive=attributes.PASSIVE_NO_INITIALIZE)
  1326. if existing_version is not attributes.PASSIVE_NO_RESULT and \
  1327. merged_version is not attributes.PASSIVE_NO_RESULT and \
  1328. existing_version != merged_version:
  1329. raise exc.StaleDataError(
  1330. "Version id '%s' on merged state %s "
  1331. "does not match existing version '%s'. "
  1332. "Leave the version attribute unset when "
  1333. "merging to update the most recent version."
  1334. % (
  1335. existing_version,
  1336. state_str(merged_state),
  1337. merged_version
  1338. ))
  1339. merged_state.load_path = state.load_path
  1340. merged_state.load_options = state.load_options
  1341. for prop in mapper.iterate_properties:
  1342. prop.merge(self, state, state_dict,
  1343. merged_state, merged_dict,
  1344. load, _recursive)
  1345. if not load:
  1346. # remove any history
  1347. merged_state._commit_all(merged_dict, self.identity_map)
  1348. if new_instance:
  1349. merged_state.manager.dispatch.load(merged_state, None)
  1350. return merged
  1351. def _validate_persistent(self, state):
  1352. if not self.identity_map.contains_state(state):
  1353. raise sa_exc.InvalidRequestError(
  1354. "Instance '%s' is not persistent within this Session" %
  1355. state_str(state))
  1356. def _save_impl(self, state):
  1357. if state.key is not None:
  1358. raise sa_exc.InvalidRequestError(
  1359. "Object '%s' already has an identity - it can't be registered "
  1360. "as pending" % state_str(state))
  1361. self._before_attach(state)
  1362. if state not in self._new:
  1363. self._new[state] = state.obj()
  1364. state.insert_order = len(self._new)
  1365. self._attach(state)
  1366. def _update_impl(self, state, discard_existing=False):
  1367. if (self.identity_map.contains_state(state) and
  1368. state not in self._deleted):
  1369. return
  1370. if state.key is None:
  1371. raise sa_exc.InvalidRequestError(
  1372. "Instance '%s' is not persisted" %
  1373. state_str(state))
  1374. if state.deleted:
  1375. raise sa_exc.InvalidRequestError(
  1376. "Instance '%s' has been deleted. Use the make_transient() "
  1377. "function to send this object back to the transient state." %
  1378. state_str(state)
  1379. )
  1380. self._before_attach(state)
  1381. self._deleted.pop(state, None)
  1382. if discard_existing:
  1383. self.identity_map.replace(state)
  1384. else:
  1385. self.identity_map.add(state)
  1386. self._attach(state)
  1387. def _save_or_update_impl(self, state):
  1388. if state.key is None:
  1389. self._save_impl(state)
  1390. else:
  1391. self._update_impl(state)
  1392. def _delete_impl(self, state):
  1393. if state in self._deleted:
  1394. return
  1395. if state.key is None:
  1396. return
  1397. self._attach(state, include_before=True)
  1398. self._deleted[state] = state.obj()
  1399. self.identity_map.add(state)
  1400. def enable_relationship_loading(self, obj):
  1401. """Associate an object with this :class:`.Session` for related
  1402. object loading.
  1403. .. warning::
  1404. :meth:`.enable_relationship_loading` exists to serve special
  1405. use cases and is not recommended for general use.
  1406. Accesses of attributes mapped with :func:`.relationship`
  1407. will attempt to load a value from the database using this
  1408. :class:`.Session` as the source of connectivity. The values
  1409. will be loaded based on foreign key values present on this
  1410. object - it follows that this functionality
  1411. generally only works for many-to-one-relationships.
  1412. The object will be attached to this session, but will
  1413. **not** participate in any persistence operations; its state
  1414. for almost all purposes will remain either "transient" or
  1415. "detached", except for the case of relationship loading.
  1416. Also note that backrefs will often not work as expected.
  1417. Altering a relationship-bound attribute on the target object
  1418. may not fire off a backref event, if the effective value
  1419. is what was already loaded from a foreign-key-holding value.
  1420. The :meth:`.Session.enable_relationship_loading` method is
  1421. similar to the ``load_on_pending`` flag on :func:`.relationship`. Unlike
  1422. that flag, :meth:`.Session.enable_relationship_loading` allows
  1423. an object to remain transient while still being able to load
  1424. related items.
  1425. To make a transient object associated with a :class:`.Session`
  1426. via :meth:`.Session.enable_relationship_loading` pending, add
  1427. it to the :class:`.Session` using :meth:`.Session.add` normally.
  1428. :meth:`.Session.enable_relationship_loading` does not improve
  1429. behavior when the ORM is used normally - object references should be
  1430. constructed at the object level, not at the foreign key level, so
  1431. that they are present in an ordinary way before flush()
  1432. proceeds. This method is not intended for general use.
  1433. .. versionadded:: 0.8
  1434. .. seealso::
  1435. ``load_on_pending`` at :func:`.relationship` - this flag
  1436. allows per-relationship loading of many-to-ones on items that
  1437. are pending.
  1438. """
  1439. state = attributes.instance_state(obj)
  1440. self._attach(state, include_before=True)
  1441. state._load_pending = True
  1442. def _before_attach(self, state):
  1443. if state.session_id != self.hash_key and \
  1444. self.dispatch.before_attach:
  1445. self.dispatch.before_attach(self, state.obj())
  1446. def _attach(self, state, include_before=False):
  1447. if state.key and \
  1448. state.key in self.identity_map and \
  1449. not self.identity_map.contains_state(state):
  1450. raise sa_exc.InvalidRequestError("Can't attach instance "
  1451. "%s; another instance with key %s is already "
  1452. "present in this session."
  1453. % (state_str(state), state.key))
  1454. if state.session_id and \
  1455. state.session_id is not self.hash_key and \
  1456. state.session_id in _sessions:
  1457. raise sa_exc.InvalidRequestError(
  1458. "Object '%s' is already attached to session '%s' "
  1459. "(this is '%s')" % (state_str(state),
  1460. state.session_id, self.hash_key))
  1461. if state.session_id != self.hash_key:
  1462. if include_before and \
  1463. self.dispatch.before_attach:
  1464. self.dispatch.before_attach(self, state.obj())
  1465. state.session_id = self.hash_key
  1466. if state.modified and state._strong_obj is None:
  1467. state._strong_obj = state.obj()
  1468. if self.dispatch.after_attach:
  1469. self.dispatch.after_attach(self, state.obj())
  1470. def __contains__(self, instance):
  1471. """Return True if the instance is associated with this session.
  1472. The instance may be pending or persistent within the Session for a
  1473. result of True.
  1474. """
  1475. try:
  1476. state = attributes.instance_state(instance)
  1477. except exc.NO_STATE:
  1478. raise exc.UnmappedInstanceError(instance)
  1479. return self._contains_state(state)
  1480. def __iter__(self):
  1481. """Iterate over all pending or persistent instances within this
  1482. Session.
  1483. """
  1484. return iter(list(self._new.values()) + list(self.identity_map.values()))
  1485. def _contains_state(self, state):
  1486. return state in self._new or self.identity_map.contains_state(state)
  1487. def flush(self, objects=None):
  1488. """Flush all the object changes to the database.
  1489. Writes out all pending object creations, deletions and modifications
  1490. to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are
  1491. automatically ordered by the Session's unit of work dependency
  1492. solver.
  1493. Database operations will be issued in the current transactional
  1494. context and do not affect the state of the transaction, unless an
  1495. error occurs, in which case the entire transaction is rolled back.
  1496. You may flush() as often as you like within a transaction to move
  1497. changes from Python to the database's transaction buffer.
  1498. For ``autocommit`` Sessions with no active manual transaction, flush()
  1499. will create a transaction on the fly that surrounds the entire set of
  1500. operations int the flush.
  1501. :param objects: Optional; restricts the flush operation to operate
  1502. only on elements that are in the given collection.
  1503. This feature is for an extremely narrow set of use cases where
  1504. particular objects may need to be operated upon before the
  1505. full flush() occurs. It is not intended for general use.
  1506. """
  1507. if self._flushing:
  1508. raise sa_exc.InvalidRequestError("Session is already flushing")
  1509. if self._is_clean():
  1510. return
  1511. try:
  1512. self._flushing = True
  1513. self._flush(objects)
  1514. finally:
  1515. self._flushing = False
  1516. def _flush_warning(self, method):
  1517. util.warn(
  1518. "Usage of the '%s' operation is not currently supported "
  1519. "within the execution stage of the flush process. "
  1520. "Results may not be consistent. Consider using alternative "
  1521. "event listeners or connection-level operations instead."
  1522. % method)
  1523. def _is_clean(self):
  1524. return not self.identity_map.check_modified() and \
  1525. not self._deleted and \
  1526. not self._new
  1527. def _flush(self, objects=None):
  1528. dirty = self._dirty_states
  1529. if not dirty and not self._deleted and not self._new:
  1530. self.identity_map._modified.clear()
  1531. return
  1532. flush_context = UOWTransaction(self)
  1533. if self.dispatch.before_flush:
  1534. self.dispatch.before_flush(self, flush_context, objects)
  1535. # re-establish "dirty states" in case the listeners
  1536. # added
  1537. dirty = self._dirty_states
  1538. deleted = set(self._deleted)
  1539. new = set(self._new)
  1540. dirty = set(dirty).difference(deleted)
  1541. # create the set of all objects we want to operate upon
  1542. if objects:
  1543. # specific list passed in
  1544. objset = set()
  1545. for o in objects:
  1546. try:
  1547. state = attributes.instance_state(o)
  1548. except exc.NO_STATE:
  1549. raise exc.UnmappedInstanceError(o)
  1550. objset.add(state)
  1551. else:
  1552. objset = None
  1553. # store objects whose fate has been decided
  1554. processed = set()
  1555. # put all saves/updates into the flush context. detect top-level
  1556. # orphans and throw them into deleted.
  1557. if objset:
  1558. proc = new.union(dirty).intersection(objset).difference(deleted)
  1559. else:
  1560. proc = new.union(dirty).difference(deleted)
  1561. for state in proc:
  1562. is_orphan = (
  1563. _state_mapper(state)._is_orphan(state) and state.has_identity)
  1564. flush_context.register_object(state, isdelete=is_orphan)
  1565. processed.add(state)
  1566. # put all remaining deletes into the flush context.
  1567. if objset:
  1568. proc = deleted.intersection(objset).difference(processed)
  1569. else:
  1570. proc = deleted.difference(processed)
  1571. for state in proc:
  1572. flush_context.register_object(state, isdelete=True)
  1573. if not flush_context.has_work:
  1574. return
  1575. flush_context.transaction = transaction = self.begin(
  1576. subtransactions=True)
  1577. try:
  1578. self._warn_on_events = True
  1579. try:
  1580. flush_context.execute()
  1581. finally:
  1582. self._warn_on_events = False
  1583. self.dispatch.after_flush(self, flush_context)
  1584. flush_context.finalize_flush_changes()
  1585. if not objects and self.identity_map._modified:
  1586. len_ = len(self.identity_map._modified)
  1587. statelib.InstanceState._commit_all_states(
  1588. [(state, state.dict) for state in
  1589. self.identity_map._modified],
  1590. instance_dict=self.identity_map)
  1591. util.warn("Attribute history events accumulated on %d "
  1592. "previously clean instances "
  1593. "within inner-flush event handlers have been reset, "
  1594. "and will not result in database updates. "
  1595. "Consider using set_committed_value() within "
  1596. "inner-flush event handlers to avoid this warning."
  1597. % len_)
  1598. # useful assertions:
  1599. #if not objects:
  1600. # assert not self.identity_map._modified
  1601. #else:
  1602. # assert self.identity_map._modified == \
  1603. # self.identity_map._modified.difference(objects)
  1604. self.dispatch.after_flush_postexec(self, flush_context)
  1605. transaction.commit()
  1606. except:
  1607. with util.safe_reraise():
  1608. transaction.rollback(_capture_exception=True)
  1609. def is_modified(self, instance, include_collections=True,
  1610. passive=True):
  1611. """Return ``True`` if the given instance has locally
  1612. modified attributes.
  1613. This method retrieves the history for each instrumented
  1614. attribute on the instance and performs a comparison of the current
  1615. value to its previously committed value, if any.
  1616. It is in effect a more expensive and accurate
  1617. version of checking for the given instance in the
  1618. :attr:`.Session.dirty` collection; a full test for
  1619. each attribute's net "dirty" status is performed.
  1620. E.g.::
  1621. return session.is_modified(someobject)
  1622. .. versionchanged:: 0.8
  1623. When using SQLAlchemy 0.7 and earlier, the ``passive``
  1624. flag should **always** be explicitly set to ``True``,
  1625. else SQL loads/autoflushes may proceed which can affect
  1626. the modified state itself:
  1627. ``session.is_modified(someobject, passive=True)``\ .
  1628. In 0.8 and above, the behavior is corrected and
  1629. this flag is ignored.
  1630. A few caveats to this method apply:
  1631. * Instances present in the :attr:`.Session.dirty` collection may report
  1632. ``False`` when tested with this method. This is because
  1633. the object may have received change events via attribute
  1634. mutation, thus placing it in :attr:`.Session.dirty`,
  1635. but ultimately the state is the same as that loaded from
  1636. the database, resulting in no net change here.
  1637. * Scalar attributes may not have recorded the previously set
  1638. value when a new value was applied, if the attribute was not loaded,
  1639. or was expired, at the time the new value was received - in these
  1640. cases, the attribute is assumed to have a change, even if there is
  1641. ultimately no net change against its database value. SQLAlchemy in
  1642. most cases does not need the "old" value when a set event occurs, so
  1643. it skips the expense of a SQL call if the old value isn't present,
  1644. based on the assumption that an UPDATE of the scalar value is
  1645. usually needed, and in those few cases where it isn't, is less
  1646. expensive on average than issuing a defensive SELECT.
  1647. The "old" value is fetched unconditionally upon set only if the
  1648. attribute container has the ``active_history`` flag set to ``True``.
  1649. This flag is set typically for primary key attributes and scalar
  1650. object references that are not a simple many-to-one. To set this
  1651. flag for any arbitrary mapped column, use the ``active_history``
  1652. argument with :func:`.column_property`.
  1653. :param instance: mapped instance to be tested for pending changes.
  1654. :param include_collections: Indicates if multivalued collections
  1655. should be included in the operation. Setting this to ``False`` is a
  1656. way to detect only local-column based properties (i.e. scalar columns
  1657. or many-to-one foreign keys) that would result in an UPDATE for this
  1658. instance upon flush.
  1659. :param passive:
  1660. .. versionchanged:: 0.8
  1661. Ignored for backwards compatibility.
  1662. When using SQLAlchemy 0.7 and earlier, this flag should always
  1663. be set to ``True``.
  1664. """
  1665. state = object_state(instance)
  1666. if not state.modified:
  1667. return False
  1668. dict_ = state.dict
  1669. for attr in state.manager.attributes:
  1670. if \
  1671. (
  1672. not include_collections and
  1673. hasattr(attr.impl, 'get_collection')
  1674. ) or not hasattr(attr.impl, 'get_history'):
  1675. continue
  1676. (added, unchanged, deleted) = \
  1677. attr.impl.get_history(state, dict_,
  1678. passive=attributes.NO_CHANGE)
  1679. if added or deleted:
  1680. return True
  1681. else:
  1682. return False
  1683. @property
  1684. def is_active(self):
  1685. """True if this :class:`.Session` is in "transaction mode" and
  1686. is not in "partial rollback" state.
  1687. The :class:`.Session` in its default mode of ``autocommit=False``
  1688. is essentially always in "transaction mode", in that a
  1689. :class:`.SessionTransaction` is associated with it as soon as
  1690. it is instantiated. This :class:`.SessionTransaction` is immediately
  1691. replaced with a new one as soon as it is ended, due to a rollback,
  1692. commit, or close operation.
  1693. "Transaction mode" does *not* indicate whether
  1694. or not actual database connection resources are in use; the
  1695. :class:`.SessionTransaction` object coordinates among zero or more
  1696. actual database transactions, and starts out with none, accumulating
  1697. individual DBAPI connections as different data sources are used
  1698. within its scope. The best way to track when a particular
  1699. :class:`.Session` has actually begun to use DBAPI resources is to
  1700. implement a listener using the :meth:`.SessionEvents.after_begin`
  1701. method, which will deliver both the :class:`.Session` as well as the
  1702. target :class:`.Connection` to a user-defined event listener.
  1703. The "partial rollback" state refers to when an "inner" transaction,
  1704. typically used during a flush, encounters an error and emits a
  1705. rollback of the DBAPI connection. At this point, the
  1706. :class:`.Session` is in "partial rollback" and awaits for the user to
  1707. call :meth:`.Session.rollback`, in order to close out the
  1708. transaction stack. It is in this "partial rollback" period that the
  1709. :attr:`.is_active` flag returns False. After the call to
  1710. :meth:`.Session.rollback`, the :class:`.SessionTransaction` is replaced
  1711. with a new one and :attr:`.is_active` returns ``True`` again.
  1712. When a :class:`.Session` is used in ``autocommit=True`` mode, the
  1713. :class:`.SessionTransaction` is only instantiated within the scope
  1714. of a flush call, or when :meth:`.Session.begin` is called. So
  1715. :attr:`.is_active` will always be ``False`` outside of a flush or
  1716. :meth:`.Session.begin` block in this mode, and will be ``True``
  1717. within the :meth:`.Session.begin` block as long as it doesn't enter
  1718. "partial rollback" state.
  1719. From all the above, it follows that the only purpose to this flag is
  1720. for application frameworks that wish to detect is a "rollback" is
  1721. necessary within a generic error handling routine, for
  1722. :class:`.Session` objects that would otherwise be in
  1723. "partial rollback" mode. In a typical integration case, this is also
  1724. not necessary as it is standard practice to emit
  1725. :meth:`.Session.rollback` unconditionally within the outermost
  1726. exception catch.
  1727. To track the transactional state of a :class:`.Session` fully,
  1728. use event listeners, primarily the :meth:`.SessionEvents.after_begin`,
  1729. :meth:`.SessionEvents.after_commit`,
  1730. :meth:`.SessionEvents.after_rollback` and related events.
  1731. """
  1732. return self.transaction and self.transaction.is_active
  1733. identity_map = None
  1734. """A mapping of object identities to objects themselves.
  1735. Iterating through ``Session.identity_map.values()`` provides
  1736. access to the full set of persistent objects (i.e., those
  1737. that have row identity) currently in the session.
  1738. .. seealso::
  1739. :func:`.identity_key` - helper function to produce the keys used
  1740. in this dictionary.
  1741. """
  1742. @property
  1743. def _dirty_states(self):
  1744. """The set of all persistent states considered dirty.
  1745. This method returns all states that were modified including
  1746. those that were possibly deleted.
  1747. """
  1748. return self.identity_map._dirty_states()
  1749. @property
  1750. def dirty(self):
  1751. """The set of all persistent instances considered dirty.
  1752. E.g.::
  1753. some_mapped_object in session.dirty
  1754. Instances are considered dirty when they were modified but not
  1755. deleted.
  1756. Note that this 'dirty' calculation is 'optimistic'; most
  1757. attribute-setting or collection modification operations will
  1758. mark an instance as 'dirty' and place it in this set, even if
  1759. there is no net change to the attribute's value. At flush
  1760. time, the value of each attribute is compared to its
  1761. previously saved value, and if there's no net change, no SQL
  1762. operation will occur (this is a more expensive operation so
  1763. it's only done at flush time).
  1764. To check if an instance has actionable net changes to its
  1765. attributes, use the :meth:`.Session.is_modified` method.
  1766. """
  1767. return util.IdentitySet(
  1768. [state.obj()
  1769. for state in self._dirty_states
  1770. if state not in self._deleted])
  1771. @property
  1772. def deleted(self):
  1773. "The set of all instances marked as 'deleted' within this ``Session``"
  1774. return util.IdentitySet(list(self._deleted.values()))
  1775. @property
  1776. def new(self):
  1777. "The set of all instances marked as 'new' within this ``Session``."
  1778. return util.IdentitySet(list(self._new.values()))
  1779. class sessionmaker(_SessionClassMethods):
  1780. """A configurable :class:`.Session` factory.
  1781. The :class:`.sessionmaker` factory generates new
  1782. :class:`.Session` objects when called, creating them given
  1783. the configurational arguments established here.
  1784. e.g.::
  1785. # global scope
  1786. Session = sessionmaker(autoflush=False)
  1787. # later, in a local scope, create and use a session:
  1788. sess = Session()
  1789. Any keyword arguments sent to the constructor itself will override the
  1790. "configured" keywords::
  1791. Session = sessionmaker()
  1792. # bind an individual session to a connection
  1793. sess = Session(bind=connection)
  1794. The class also includes a method :meth:`.configure`, which can
  1795. be used to specify additional keyword arguments to the factory, which
  1796. will take effect for subsequent :class:`.Session` objects generated.
  1797. This is usually used to associate one or more :class:`.Engine` objects
  1798. with an existing :class:`.sessionmaker` factory before it is first
  1799. used::
  1800. # application starts
  1801. Session = sessionmaker()
  1802. # ... later
  1803. engine = create_engine('sqlite:///foo.db')
  1804. Session.configure(bind=engine)
  1805. sess = Session()
  1806. .. seealso:
  1807. :ref:`session_getting` - introductory text on creating
  1808. sessions using :class:`.sessionmaker`.
  1809. """
  1810. def __init__(self, bind=None, class_=Session, autoflush=True,
  1811. autocommit=False,
  1812. expire_on_commit=True,
  1813. info=None, **kw):
  1814. """Construct a new :class:`.sessionmaker`.
  1815. All arguments here except for ``class_`` correspond to arguments
  1816. accepted by :class:`.Session` directly. See the
  1817. :meth:`.Session.__init__` docstring for more details on parameters.
  1818. :param bind: a :class:`.Engine` or other :class:`.Connectable` with
  1819. which newly created :class:`.Session` objects will be associated.
  1820. :param class_: class to use in order to create new :class:`.Session`
  1821. objects. Defaults to :class:`.Session`.
  1822. :param autoflush: The autoflush setting to use with newly created
  1823. :class:`.Session` objects.
  1824. :param autocommit: The autocommit setting to use with newly created
  1825. :class:`.Session` objects.
  1826. :param expire_on_commit=True: the expire_on_commit setting to use
  1827. with newly created :class:`.Session` objects.
  1828. :param info: optional dictionary of information that will be available
  1829. via :attr:`.Session.info`. Note this dictionary is *updated*, not
  1830. replaced, when the ``info`` parameter is specified to the specific
  1831. :class:`.Session` construction operation.
  1832. .. versionadded:: 0.9.0
  1833. :param \**kw: all other keyword arguments are passed to the constructor
  1834. of newly created :class:`.Session` objects.
  1835. """
  1836. kw['bind'] = bind
  1837. kw['autoflush'] = autoflush
  1838. kw['autocommit'] = autocommit
  1839. kw['expire_on_commit'] = expire_on_commit
  1840. if info is not None:
  1841. kw['info'] = info
  1842. self.kw = kw
  1843. # make our own subclass of the given class, so that
  1844. # events can be associated with it specifically.
  1845. self.class_ = type(class_.__name__, (class_,), {})
  1846. def __call__(self, **local_kw):
  1847. """Produce a new :class:`.Session` object using the configuration
  1848. established in this :class:`.sessionmaker`.
  1849. In Python, the ``__call__`` method is invoked on an object when
  1850. it is "called" in the same way as a function::
  1851. Session = sessionmaker()
  1852. session = Session() # invokes sessionmaker.__call__()
  1853. """
  1854. for k, v in self.kw.items():
  1855. if k == 'info' and 'info' in local_kw:
  1856. d = v.copy()
  1857. d.update(local_kw['info'])
  1858. local_kw['info'] = d
  1859. else:
  1860. local_kw.setdefault(k, v)
  1861. return self.class_(**local_kw)
  1862. def configure(self, **new_kw):
  1863. """(Re)configure the arguments for this sessionmaker.
  1864. e.g.::
  1865. Session = sessionmaker()
  1866. Session.configure(bind=create_engine('sqlite://'))
  1867. """
  1868. self.kw.update(new_kw)
  1869. def __repr__(self):
  1870. return "%s(class_=%r,%s)" % (
  1871. self.__class__.__name__,
  1872. self.class_.__name__,
  1873. ", ".join("%s=%r" % (k, v) for k, v in self.kw.items())
  1874. )
  1875. def make_transient(instance):
  1876. """Make the given instance 'transient'.
  1877. This will remove its association with any
  1878. session and additionally will remove its "identity key",
  1879. such that it's as though the object were newly constructed,
  1880. except retaining its values. It also resets the
  1881. "deleted" flag on the state if this object
  1882. had been explicitly deleted by its session.
  1883. Attributes which were "expired" or deferred at the
  1884. instance level are reverted to undefined, and
  1885. will not trigger any loads.
  1886. """
  1887. state = attributes.instance_state(instance)
  1888. s = _state_session(state)
  1889. if s:
  1890. s._expunge_state(state)
  1891. # remove expired state and
  1892. # deferred callables
  1893. state.callables.clear()
  1894. if state.key:
  1895. del state.key
  1896. if state.deleted:
  1897. del state.deleted
  1898. def make_transient_to_detached(instance):
  1899. """Make the given transient instance 'detached'.
  1900. All attribute history on the given instance
  1901. will be reset as though the instance were freshly loaded
  1902. from a query. Missing attributes will be marked as expired.
  1903. The primary key attributes of the object, which are required, will be made
  1904. into the "key" of the instance.
  1905. The object can then be added to a session, or merged
  1906. possibly with the load=False flag, at which point it will look
  1907. as if it were loaded that way, without emitting SQL.
  1908. This is a special use case function that differs from a normal
  1909. call to :meth:`.Session.merge` in that a given persistent state
  1910. can be manufactured without any SQL calls.
  1911. .. versionadded:: 0.9.5
  1912. .. seealso::
  1913. :func:`.make_transient`
  1914. """
  1915. state = attributes.instance_state(instance)
  1916. if state.session_id or state.key:
  1917. raise sa_exc.InvalidRequestError(
  1918. "Given object must be transient")
  1919. state.key = state.mapper._identity_key_from_state(state)
  1920. if state.deleted:
  1921. del state.deleted
  1922. state._commit_all(state.dict)
  1923. state._expire_attributes(state.dict, state.unloaded)
  1924. def object_session(instance):
  1925. """Return the ``Session`` to which instance belongs.
  1926. If the instance is not a mapped instance, an error is raised.
  1927. """
  1928. try:
  1929. return _state_session(attributes.instance_state(instance))
  1930. except exc.NO_STATE:
  1931. raise exc.UnmappedInstanceError(instance)
  1932. _new_sessionid = util.counter()