Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/orm/session.py : 44%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# orm/session.py
2# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: http://www.opensource.org/licenses/mit-license.php
7"""Provides the Session class and related utilities."""
10import itertools
11import sys
12import weakref
14from . import attributes
15from . import exc
16from . import identity
17from . import loading
18from . import persistence
19from . import query
20from . import state as statelib
21from .base import _class_to_mapper
22from .base import _none_set
23from .base import _state_mapper
24from .base import instance_str
25from .base import object_mapper
26from .base import object_state
27from .base import state_str
28from .deprecated_interfaces import SessionExtension
29from .unitofwork import UOWTransaction
30from .. import engine
31from .. import exc as sa_exc
32from .. import sql
33from .. import util
34from ..inspection import inspect
35from ..sql import expression
36from ..sql import util as sql_util
39__all__ = ["Session", "SessionTransaction", "SessionExtension", "sessionmaker"]
41_sessions = weakref.WeakValueDictionary()
42"""Weak-referencing dictionary of :class:`.Session` objects.
43"""
46def _state_session(state):
47 """Given an :class:`.InstanceState`, return the :class:`.Session`
48 associated, if any.
49 """
50 if state.session_id:
51 try:
52 return _sessions[state.session_id]
53 except KeyError:
54 pass
55 return None
58class _SessionClassMethods(object):
59 """Class-level methods for :class:`.Session`, :class:`.sessionmaker`."""
61 @classmethod
62 @util.deprecated(
63 "1.3",
64 "The :meth:`.Session.close_all` method is deprecated and will be "
65 "removed in a future release. Please refer to "
66 ":func:`.session.close_all_sessions`.",
67 )
68 def close_all(cls):
69 """Close *all* sessions in memory."""
71 close_all_sessions()
73 @classmethod
74 @util.dependencies("sqlalchemy.orm.util")
75 def identity_key(cls, orm_util, *args, **kwargs):
76 """Return an identity key.
78 This is an alias of :func:`.util.identity_key`.
80 """
81 return orm_util.identity_key(*args, **kwargs)
83 @classmethod
84 def object_session(cls, instance):
85 """Return the :class:`.Session` to which an object belongs.
87 This is an alias of :func:`.object_session`.
89 """
91 return object_session(instance)
94ACTIVE = util.symbol("ACTIVE")
95PREPARED = util.symbol("PREPARED")
96COMMITTED = util.symbol("COMMITTED")
97DEACTIVE = util.symbol("DEACTIVE")
98CLOSED = util.symbol("CLOSED")
101class SessionTransaction(object):
102 """A :class:`.Session`-level transaction.
104 :class:`.SessionTransaction` is a mostly behind-the-scenes object
105 not normally referenced directly by application code. It coordinates
106 among multiple :class:`_engine.Connection` objects, maintaining a database
107 transaction for each one individually, committing or rolling them
108 back all at once. It also provides optional two-phase commit behavior
109 which can augment this coordination operation.
111 The :attr:`.Session.transaction` attribute of :class:`.Session`
112 refers to the current :class:`.SessionTransaction` object in use, if any.
113 The :attr:`.SessionTransaction.parent` attribute refers to the parent
114 :class:`.SessionTransaction` in the stack of :class:`.SessionTransaction`
115 objects. If this attribute is ``None``, then this is the top of the stack.
116 If non-``None``, then this :class:`.SessionTransaction` refers either
117 to a so-called "subtransaction" or a "nested" transaction. A
118 "subtransaction" is a scoping concept that demarcates an inner portion
119 of the outermost "real" transaction. A nested transaction, which
120 is indicated when the :attr:`.SessionTransaction.nested`
121 attribute is also True, indicates that this :class:`.SessionTransaction`
122 corresponds to a SAVEPOINT.
124 **Life Cycle**
126 A :class:`.SessionTransaction` is associated with a :class:`.Session`
127 in its default mode of ``autocommit=False`` immediately, associated
128 with no database connections. As the :class:`.Session` is called upon
129 to emit SQL on behalf of various :class:`_engine.Engine` or
130 :class:`_engine.Connection`
131 objects, a corresponding :class:`_engine.Connection` and associated
132 :class:`.Transaction` is added to a collection within the
133 :class:`.SessionTransaction` object, becoming one of the
134 connection/transaction pairs maintained by the
135 :class:`.SessionTransaction`. The start of a :class:`.SessionTransaction`
136 can be tracked using the :meth:`.SessionEvents.after_transaction_create`
137 event.
139 The lifespan of the :class:`.SessionTransaction` ends when the
140 :meth:`.Session.commit`, :meth:`.Session.rollback` or
141 :meth:`.Session.close` methods are called. At this point, the
142 :class:`.SessionTransaction` removes its association with its parent
143 :class:`.Session`. A :class:`.Session` that is in ``autocommit=False``
144 mode will create a new :class:`.SessionTransaction` to replace it
145 immediately, whereas a :class:`.Session` that's in ``autocommit=True``
146 mode will remain without a :class:`.SessionTransaction` until the
147 :meth:`.Session.begin` method is called. The end of a
148 :class:`.SessionTransaction` can be tracked using the
149 :meth:`.SessionEvents.after_transaction_end` event.
151 **Nesting and Subtransactions**
153 Another detail of :class:`.SessionTransaction` behavior is that it is
154 capable of "nesting". This means that the :meth:`.Session.begin` method
155 can be called while an existing :class:`.SessionTransaction` is already
156 present, producing a new :class:`.SessionTransaction` that temporarily
157 replaces the parent :class:`.SessionTransaction`. When a
158 :class:`.SessionTransaction` is produced as nested, it assigns itself to
159 the :attr:`.Session.transaction` attribute, and it additionally will assign
160 the previous :class:`.SessionTransaction` to its :attr:`.Session.parent`
161 attribute. The behavior is effectively a
162 stack, where :attr:`.Session.transaction` refers to the current head of
163 the stack, and the :attr:`.SessionTransaction.parent` attribute allows
164 traversal up the stack until :attr:`.SessionTransaction.parent` is
165 ``None``, indicating the top of the stack.
167 When the scope of :class:`.SessionTransaction` is ended via
168 :meth:`.Session.commit` or :meth:`.Session.rollback`, it restores its
169 parent :class:`.SessionTransaction` back onto the
170 :attr:`.Session.transaction` attribute.
172 The purpose of this stack is to allow nesting of
173 :meth:`.Session.rollback` or :meth:`.Session.commit` calls in context
174 with various flavors of :meth:`.Session.begin`. This nesting behavior
175 applies to when :meth:`.Session.begin_nested` is used to emit a
176 SAVEPOINT transaction, and is also used to produce a so-called
177 "subtransaction" which allows a block of code to use a
178 begin/rollback/commit sequence regardless of whether or not its enclosing
179 code block has begun a transaction. The :meth:`.flush` method, whether
180 called explicitly or via autoflush, is the primary consumer of the
181 "subtransaction" feature, in that it wishes to guarantee that it works
182 within in a transaction block regardless of whether or not the
183 :class:`.Session` is in transactional mode when the method is called.
185 Note that the flush process that occurs within the "autoflush" feature
186 as well as when the :meth:`.Session.flush` method is used **always**
187 creates a :class:`.SessionTransaction` object. This object is normally
188 a subtransaction, unless the :class:`.Session` is in autocommit mode
189 and no transaction exists at all, in which case it's the outermost
190 transaction. Any event-handling logic or other inspection logic
191 needs to take into account whether a :class:`.SessionTransaction`
192 is the outermost transaction, a subtransaction, or a "nested" / SAVEPOINT
193 transaction.
195 .. seealso::
197 :meth:`.Session.rollback`
199 :meth:`.Session.commit`
201 :meth:`.Session.begin`
203 :meth:`.Session.begin_nested`
205 :attr:`.Session.is_active`
207 :meth:`.SessionEvents.after_transaction_create`
209 :meth:`.SessionEvents.after_transaction_end`
211 :meth:`.SessionEvents.after_commit`
213 :meth:`.SessionEvents.after_rollback`
215 :meth:`.SessionEvents.after_soft_rollback`
217 """
219 _rollback_exception = None
221 def __init__(self, session, parent=None, nested=False):
222 self.session = session
223 self._connections = {}
224 self._parent = parent
225 self.nested = nested
226 self._state = ACTIVE
227 if not parent and nested:
228 raise sa_exc.InvalidRequestError(
229 "Can't start a SAVEPOINT transaction when no existing "
230 "transaction is in progress"
231 )
233 if self.session._enable_transaction_accounting:
234 self._take_snapshot()
236 self.session.dispatch.after_transaction_create(self.session, self)
238 @property
239 def parent(self):
240 """The parent :class:`.SessionTransaction` of this
241 :class:`.SessionTransaction`.
243 If this attribute is ``None``, indicates this
244 :class:`.SessionTransaction` is at the top of the stack, and
245 corresponds to a real "COMMIT"/"ROLLBACK"
246 block. If non-``None``, then this is either a "subtransaction"
247 or a "nested" / SAVEPOINT transaction. If the
248 :attr:`.SessionTransaction.nested` attribute is ``True``, then
249 this is a SAVEPOINT, and if ``False``, indicates this a subtransaction.
251 .. versionadded:: 1.0.16 - use ._parent for previous versions
253 """
254 return self._parent
256 nested = False
257 """Indicates if this is a nested, or SAVEPOINT, transaction.
259 When :attr:`.SessionTransaction.nested` is True, it is expected
260 that :attr:`.SessionTransaction.parent` will be True as well.
262 """
264 @property
265 def is_active(self):
266 return self.session is not None and self._state is ACTIVE
268 def _assert_active(
269 self,
270 prepared_ok=False,
271 rollback_ok=False,
272 deactive_ok=False,
273 closed_msg="This transaction is closed",
274 ):
275 if self._state is COMMITTED:
276 raise sa_exc.InvalidRequestError(
277 "This session is in 'committed' state; no further "
278 "SQL can be emitted within this transaction."
279 )
280 elif self._state is PREPARED:
281 if not prepared_ok:
282 raise sa_exc.InvalidRequestError(
283 "This session is in 'prepared' state; no further "
284 "SQL can be emitted within this transaction."
285 )
286 elif self._state is DEACTIVE:
287 if not deactive_ok and not rollback_ok:
288 if self._rollback_exception:
289 raise sa_exc.InvalidRequestError(
290 "This Session's transaction has been rolled back "
291 "due to a previous exception during flush."
292 " To begin a new transaction with this Session, "
293 "first issue Session.rollback()."
294 " Original exception was: %s"
295 % self._rollback_exception,
296 code="7s2a",
297 )
298 elif not deactive_ok:
299 raise sa_exc.InvalidRequestError(
300 "This session is in 'inactive' state, due to the "
301 "SQL transaction being rolled back; no further "
302 "SQL can be emitted within this transaction."
303 )
304 elif self._state is CLOSED:
305 raise sa_exc.ResourceClosedError(closed_msg)
307 @property
308 def _is_transaction_boundary(self):
309 return self.nested or not self._parent
311 def connection(self, bindkey, execution_options=None, **kwargs):
312 self._assert_active()
313 bind = self.session.get_bind(bindkey, **kwargs)
314 return self._connection_for_bind(bind, execution_options)
316 def _begin(self, nested=False):
317 self._assert_active()
318 return SessionTransaction(self.session, self, nested=nested)
320 def _iterate_self_and_parents(self, upto=None):
322 current = self
323 result = ()
324 while current:
325 result += (current,)
326 if current._parent is upto:
327 break
328 elif current._parent is None:
329 raise sa_exc.InvalidRequestError(
330 "Transaction %s is not on the active transaction list"
331 % (upto)
332 )
333 else:
334 current = current._parent
336 return result
338 def _take_snapshot(self):
339 if not self._is_transaction_boundary:
340 self._new = self._parent._new
341 self._deleted = self._parent._deleted
342 self._dirty = self._parent._dirty
343 self._key_switches = self._parent._key_switches
344 return
346 if not self.session._flushing:
347 self.session.flush()
349 self._new = weakref.WeakKeyDictionary()
350 self._deleted = weakref.WeakKeyDictionary()
351 self._dirty = weakref.WeakKeyDictionary()
352 self._key_switches = weakref.WeakKeyDictionary()
354 def _restore_snapshot(self, dirty_only=False):
355 """Restore the restoration state taken before a transaction began.
357 Corresponds to a rollback.
359 """
360 assert self._is_transaction_boundary
362 to_expunge = set(self._new).union(self.session._new)
363 self.session._expunge_states(to_expunge, to_transient=True)
365 for s, (oldkey, newkey) in self._key_switches.items():
366 # we probably can do this conditionally based on
367 # if we expunged or not, but safe_discard does that anyway
368 self.session.identity_map.safe_discard(s)
370 # restore the old key
371 s.key = oldkey
373 # now restore the object, but only if we didn't expunge
374 if s not in to_expunge:
375 self.session.identity_map.replace(s)
377 for s in set(self._deleted).union(self.session._deleted):
378 self.session._update_impl(s, revert_deletion=True)
380 assert not self.session._deleted
382 for s in self.session.identity_map.all_states():
383 if not dirty_only or s.modified or s in self._dirty:
384 s._expire(s.dict, self.session.identity_map._modified)
386 def _remove_snapshot(self):
387 """Remove the restoration state taken before a transaction began.
389 Corresponds to a commit.
391 """
392 assert self._is_transaction_boundary
394 if not self.nested and self.session.expire_on_commit:
395 for s in self.session.identity_map.all_states():
396 s._expire(s.dict, self.session.identity_map._modified)
398 statelib.InstanceState._detach_states(
399 list(self._deleted), self.session
400 )
401 self._deleted.clear()
402 elif self.nested:
403 self._parent._new.update(self._new)
404 self._parent._dirty.update(self._dirty)
405 self._parent._deleted.update(self._deleted)
406 self._parent._key_switches.update(self._key_switches)
408 def _connection_for_bind(self, bind, execution_options):
409 self._assert_active()
411 if bind in self._connections:
412 if execution_options:
413 util.warn(
414 "Connection is already established for the "
415 "given bind; execution_options ignored"
416 )
417 return self._connections[bind][0]
419 local_connect = False
420 if self._parent:
421 conn = self._parent._connection_for_bind(bind, execution_options)
422 if not self.nested:
423 return conn
424 else:
425 if isinstance(bind, engine.Connection):
426 conn = bind
427 if conn.engine in self._connections:
428 raise sa_exc.InvalidRequestError(
429 "Session already has a Connection associated for the "
430 "given Connection's Engine"
431 )
432 else:
433 conn = bind._contextual_connect()
434 local_connect = True
436 try:
437 if execution_options:
438 conn = conn.execution_options(**execution_options)
440 if self.session.twophase and self._parent is None:
441 transaction = conn.begin_twophase()
442 elif self.nested:
443 transaction = conn.begin_nested()
444 else:
445 transaction = conn.begin()
446 except:
447 # connection will not not be associated with this Session;
448 # close it immediately so that it isn't closed under GC
449 if local_connect:
450 conn.close()
451 raise
452 else:
453 self._connections[conn] = self._connections[conn.engine] = (
454 conn,
455 transaction,
456 conn is not bind,
457 )
458 self.session.dispatch.after_begin(self.session, self, conn)
459 return conn
461 def prepare(self):
462 if self._parent is not None or not self.session.twophase:
463 raise sa_exc.InvalidRequestError(
464 "'twophase' mode not enabled, or not root transaction; "
465 "can't prepare."
466 )
467 self._prepare_impl()
469 def _prepare_impl(self):
470 self._assert_active()
471 if self._parent is None or self.nested:
472 self.session.dispatch.before_commit(self.session)
474 stx = self.session.transaction
475 if stx is not self:
476 for subtransaction in stx._iterate_self_and_parents(upto=self):
477 subtransaction.commit()
479 if not self.session._flushing:
480 for _flush_guard in range(100):
481 if self.session._is_clean():
482 break
483 self.session.flush()
484 else:
485 raise exc.FlushError(
486 "Over 100 subsequent flushes have occurred within "
487 "session.commit() - is an after_flush() hook "
488 "creating new objects?"
489 )
491 if self._parent is None and self.session.twophase:
492 try:
493 for t in set(self._connections.values()):
494 t[1].prepare()
495 except:
496 with util.safe_reraise():
497 self.rollback()
499 self._state = PREPARED
501 def commit(self):
502 self._assert_active(prepared_ok=True)
503 if self._state is not PREPARED:
504 self._prepare_impl()
506 if self._parent is None or self.nested:
507 for t in set(self._connections.values()):
508 t[1].commit()
510 self._state = COMMITTED
511 self.session.dispatch.after_commit(self.session)
513 if self.session._enable_transaction_accounting:
514 self._remove_snapshot()
516 self.close()
517 return self._parent
519 def rollback(self, _capture_exception=False):
520 self._assert_active(prepared_ok=True, rollback_ok=True)
522 stx = self.session.transaction
523 if stx is not self:
524 for subtransaction in stx._iterate_self_and_parents(upto=self):
525 subtransaction.close()
527 boundary = self
528 rollback_err = None
529 if self._state in (ACTIVE, PREPARED):
530 for transaction in self._iterate_self_and_parents():
531 if transaction._parent is None or transaction.nested:
532 try:
533 for t in set(transaction._connections.values()):
534 t[1].rollback()
536 transaction._state = DEACTIVE
537 self.session.dispatch.after_rollback(self.session)
538 except:
539 rollback_err = sys.exc_info()
540 finally:
541 transaction._state = DEACTIVE
542 if self.session._enable_transaction_accounting:
543 transaction._restore_snapshot(
544 dirty_only=transaction.nested
545 )
546 boundary = transaction
547 break
548 else:
549 transaction._state = DEACTIVE
551 sess = self.session
553 if (
554 not rollback_err
555 and sess._enable_transaction_accounting
556 and not sess._is_clean()
557 ):
559 # if items were added, deleted, or mutated
560 # here, we need to re-restore the snapshot
561 util.warn(
562 "Session's state has been changed on "
563 "a non-active transaction - this state "
564 "will be discarded."
565 )
566 boundary._restore_snapshot(dirty_only=boundary.nested)
568 self.close()
570 if self._parent and _capture_exception:
571 self._parent._rollback_exception = sys.exc_info()[1]
573 if rollback_err:
574 util.raise_(rollback_err[1], with_traceback=rollback_err[2])
576 sess.dispatch.after_soft_rollback(sess, self)
578 return self._parent
580 def close(self, invalidate=False):
581 self.session.transaction = self._parent
582 if self._parent is None:
583 for connection, transaction, autoclose in set(
584 self._connections.values()
585 ):
586 if invalidate:
587 connection.invalidate()
588 if autoclose:
589 connection.close()
590 else:
591 transaction.close()
593 self._state = CLOSED
594 self.session.dispatch.after_transaction_end(self.session, self)
596 if self._parent is None:
597 if not self.session.autocommit:
598 self.session.begin()
599 self.session = None
600 self._connections = None
602 def __enter__(self):
603 return self
605 def __exit__(self, type_, value, traceback):
606 self._assert_active(deactive_ok=True, prepared_ok=True)
607 if self.session.transaction is None:
608 return
609 if type_ is None:
610 try:
611 self.commit()
612 except:
613 with util.safe_reraise():
614 self.rollback()
615 else:
616 self.rollback()
619class Session(_SessionClassMethods):
620 """Manages persistence operations for ORM-mapped objects.
622 The Session's usage paradigm is described at :doc:`/orm/session`.
625 """
627 public_methods = (
628 "__contains__",
629 "__iter__",
630 "add",
631 "add_all",
632 "begin",
633 "begin_nested",
634 "close",
635 "commit",
636 "connection",
637 "delete",
638 "execute",
639 "expire",
640 "expire_all",
641 "expunge",
642 "expunge_all",
643 "flush",
644 "get_bind",
645 "is_modified",
646 "bulk_save_objects",
647 "bulk_insert_mappings",
648 "bulk_update_mappings",
649 "merge",
650 "query",
651 "refresh",
652 "rollback",
653 "scalar",
654 )
656 @util.deprecated_params(
657 weak_identity_map=(
658 "1.0",
659 "The :paramref:`.Session.weak_identity_map` parameter as well as "
660 "the strong-referencing identity map are deprecated, and will be "
661 "removed in a future release. For the use case where objects "
662 "present in a :class:`.Session` need to be automatically strong "
663 "referenced, see the recipe at "
664 ":ref:`session_referencing_behavior` for an event-based approach "
665 "to maintaining strong identity references. ",
666 ),
667 _enable_transaction_accounting=(
668 "0.7",
669 "The :paramref:`.Session._enable_transaction_accounting` "
670 "parameter is deprecated and will be removed in a future release.",
671 ),
672 extension=(
673 "0.7",
674 ":class:`.SessionExtension` is deprecated in favor of the "
675 ":class:`.SessionEvents` listener interface. The "
676 ":paramref:`.Session.extension` parameter will be "
677 "removed in a future release.",
678 ),
679 )
680 def __init__(
681 self,
682 bind=None,
683 autoflush=True,
684 expire_on_commit=True,
685 _enable_transaction_accounting=True,
686 autocommit=False,
687 twophase=False,
688 weak_identity_map=None,
689 binds=None,
690 extension=None,
691 enable_baked_queries=True,
692 info=None,
693 query_cls=None,
694 ):
695 r"""Construct a new Session.
697 See also the :class:`.sessionmaker` function which is used to
698 generate a :class:`.Session`-producing callable with a given
699 set of arguments.
701 :param autocommit:
703 .. warning::
705 The autocommit flag is **not for general use**, and if it is
706 used, queries should only be invoked within the span of a
707 :meth:`.Session.begin` / :meth:`.Session.commit` pair. Executing
708 queries outside of a demarcated transaction is a legacy mode
709 of usage, and can in some cases lead to concurrent connection
710 checkouts.
712 Defaults to ``False``. When ``True``, the
713 :class:`.Session` does not keep a persistent transaction running,
714 and will acquire connections from the engine on an as-needed basis,
715 returning them immediately after their use. Flushes will begin and
716 commit (or possibly rollback) their own transaction if no
717 transaction is present. When using this mode, the
718 :meth:`.Session.begin` method is used to explicitly start
719 transactions.
721 .. seealso::
723 :ref:`session_autocommit`
725 :param autoflush: When ``True``, all query operations will issue a
726 :meth:`~.Session.flush` call to this ``Session`` before proceeding.
727 This is a convenience feature so that :meth:`~.Session.flush` need
728 not be called repeatedly in order for database queries to retrieve
729 results. It's typical that ``autoflush`` is used in conjunction
730 with ``autocommit=False``. In this scenario, explicit calls to
731 :meth:`~.Session.flush` are rarely needed; you usually only need to
732 call :meth:`~.Session.commit` (which flushes) to finalize changes.
734 :param bind: An optional :class:`_engine.Engine` or
735 :class:`_engine.Connection` to
736 which this ``Session`` should be bound. When specified, all SQL
737 operations performed by this session will execute via this
738 connectable.
740 :param binds: A dictionary which may specify any number of
741 :class:`_engine.Engine` or :class:`_engine.Connection`
742 objects as the source of
743 connectivity for SQL operations on a per-entity basis. The keys
744 of the dictionary consist of any series of mapped classes,
745 arbitrary Python classes that are bases for mapped classes,
746 :class:`_schema.Table` objects and :class:`_orm.Mapper` objects.
747 The
748 values of the dictionary are then instances of
749 :class:`_engine.Engine`
750 or less commonly :class:`_engine.Connection` objects.
751 Operations which
752 proceed relative to a particular mapped class will consult this
753 dictionary for the closest matching entity in order to determine
754 which :class:`_engine.Engine` should be used for a particular SQL
755 operation. The complete heuristics for resolution are
756 described at :meth:`.Session.get_bind`. Usage looks like::
758 Session = sessionmaker(binds={
759 SomeMappedClass: create_engine('postgresql://engine1'),
760 SomeDeclarativeBase: create_engine('postgresql://engine2'),
761 some_mapper: create_engine('postgresql://engine3'),
762 some_table: create_engine('postgresql://engine4'),
763 })
765 .. seealso::
767 :ref:`session_partitioning`
769 :meth:`.Session.bind_mapper`
771 :meth:`.Session.bind_table`
773 :meth:`.Session.get_bind`
776 :param \class_: Specify an alternate class other than
777 ``sqlalchemy.orm.session.Session`` which should be used by the
778 returned class. This is the only argument that is local to the
779 :class:`.sessionmaker` function, and is not sent directly to the
780 constructor for ``Session``.
782 :param enable_baked_queries: defaults to ``True``. A flag consumed
783 by the :mod:`sqlalchemy.ext.baked` extension to determine if
784 "baked queries" should be cached, as is the normal operation
785 of this extension. When set to ``False``, all caching is disabled,
786 including baked queries defined by the calling application as
787 well as those used internally. Setting this flag to ``False``
788 can significantly reduce memory use, however will also degrade
789 performance for those areas that make use of baked queries
790 (such as relationship loaders). Additionally, baked query
791 logic in the calling application or potentially within the ORM
792 that may be malfunctioning due to cache key collisions or similar
793 can be flagged by observing if this flag resolves the issue.
795 .. versionadded:: 1.2
797 :param _enable_transaction_accounting: A
798 legacy-only flag which when ``False`` disables *all* 0.5-style
799 object accounting on transaction boundaries.
801 :param expire_on_commit: Defaults to ``True``. When ``True``, all
802 instances will be fully expired after each :meth:`~.commit`,
803 so that all attribute/object access subsequent to a completed
804 transaction will load from the most recent database state.
806 :param extension: An optional
807 :class:`~.SessionExtension` instance, or a list
808 of such instances, which will receive pre- and post- commit and
809 flush events, as well as a post-rollback event.
811 :param info: optional dictionary of arbitrary data to be associated
812 with this :class:`.Session`. Is available via the
813 :attr:`.Session.info` attribute. Note the dictionary is copied at
814 construction time so that modifications to the per-
815 :class:`.Session` dictionary will be local to that
816 :class:`.Session`.
818 .. versionadded:: 0.9.0
820 :param query_cls: Class which should be used to create new Query
821 objects, as returned by the :meth:`~.Session.query` method.
822 Defaults to :class:`_query.Query`.
824 :param twophase: When ``True``, all transactions will be started as
825 a "two phase" transaction, i.e. using the "two phase" semantics
826 of the database in use along with an XID. During a
827 :meth:`~.commit`, after :meth:`~.flush` has been issued for all
828 attached databases, the :meth:`~.TwoPhaseTransaction.prepare`
829 method on each database's :class:`.TwoPhaseTransaction` will be
830 called. This allows each database to roll back the entire
831 transaction, before each transaction is committed.
833 :param weak_identity_map: Defaults to ``True`` - when set to
834 ``False``, objects placed in the :class:`.Session` will be
835 strongly referenced until explicitly removed or the
836 :class:`.Session` is closed.
839 """
841 if weak_identity_map in (True, None):
842 self._identity_cls = identity.WeakInstanceDict
843 else:
844 self._identity_cls = identity.StrongInstanceDict
846 self.identity_map = self._identity_cls()
848 self._new = {} # InstanceState->object, strong refs object
849 self._deleted = {} # same
850 self.bind = bind
851 self.__binds = {}
852 self._flushing = False
853 self._warn_on_events = False
854 self.transaction = None
855 self.hash_key = _new_sessionid()
856 self.autoflush = autoflush
857 self.autocommit = autocommit
858 self.expire_on_commit = expire_on_commit
859 self.enable_baked_queries = enable_baked_queries
860 self._enable_transaction_accounting = _enable_transaction_accounting
862 self.twophase = twophase
863 self._query_cls = query_cls if query_cls else query.Query
864 if info:
865 self.info.update(info)
867 if extension:
868 for ext in util.to_list(extension):
869 SessionExtension._adapt_listener(self, ext)
871 if binds is not None:
872 for key, bind in binds.items():
873 self._add_bind(key, bind)
875 if not self.autocommit:
876 self.begin()
877 _sessions[self.hash_key] = self
879 connection_callable = None
881 transaction = None
882 """The current active or inactive :class:`.SessionTransaction`."""
884 @util.memoized_property
885 def info(self):
886 """A user-modifiable dictionary.
888 The initial value of this dictionary can be populated using the
889 ``info`` argument to the :class:`.Session` constructor or
890 :class:`.sessionmaker` constructor or factory methods. The dictionary
891 here is always local to this :class:`.Session` and can be modified
892 independently of all other :class:`.Session` objects.
894 .. versionadded:: 0.9.0
896 """
897 return {}
899 def begin(self, subtransactions=False, nested=False):
900 """Begin a transaction on this :class:`.Session`.
902 .. warning::
904 The :meth:`.Session.begin` method is part of a larger pattern
905 of use with the :class:`.Session` known as **autocommit mode**.
906 This is essentially a **legacy mode of use** and is
907 not necessary for new applications. The :class:`.Session`
908 normally handles the work of "begin" transparently, which in
909 turn relies upon the Python DBAPI to transparently "begin"
910 transactions; there is **no need to explicitly begin transactions**
911 when using modern :class:`.Session` programming patterns.
912 In its default mode of ``autocommit=False``, the
913 :class:`.Session` does all of its work within
914 the context of a transaction, so as soon as you call
915 :meth:`.Session.commit`, the next transaction is implicitly
916 started when the next database operation is invoked. See
917 :ref:`session_autocommit` for further background.
919 The method will raise an error if this :class:`.Session` is already
920 inside of a transaction, unless
921 :paramref:`~.Session.begin.subtransactions` or
922 :paramref:`~.Session.begin.nested` are specified. A "subtransaction"
923 is essentially a code embedding pattern that does not affect the
924 transactional state of the database connection unless a rollback is
925 emitted, in which case the whole transaction is rolled back. For
926 documentation on subtransactions, please see
927 :ref:`session_subtransactions`.
929 :param subtransactions: if True, indicates that this
930 :meth:`~.Session.begin` can create a "subtransaction".
932 :param nested: if True, begins a SAVEPOINT transaction and is
933 equivalent to calling :meth:`~.Session.begin_nested`. For
934 documentation on SAVEPOINT transactions, please see
935 :ref:`session_begin_nested`.
937 :return: the :class:`.SessionTransaction` object. Note that
938 :class:`.SessionTransaction`
939 acts as a Python context manager, allowing :meth:`.Session.begin`
940 to be used in a "with" block. See :ref:`session_autocommit` for
941 an example.
943 .. seealso::
945 :ref:`session_autocommit`
947 :meth:`.Session.begin_nested`
950 """
951 if self.transaction is not None:
952 if subtransactions or nested:
953 self.transaction = self.transaction._begin(nested=nested)
954 else:
955 raise sa_exc.InvalidRequestError(
956 "A transaction is already begun. Use "
957 "subtransactions=True to allow subtransactions."
958 )
959 else:
960 self.transaction = SessionTransaction(self, nested=nested)
961 return self.transaction # needed for __enter__/__exit__ hook
963 def begin_nested(self):
964 """Begin a "nested" transaction on this Session, e.g. SAVEPOINT.
966 The target database(s) and associated drivers must support SQL
967 SAVEPOINT for this method to function correctly.
969 For documentation on SAVEPOINT
970 transactions, please see :ref:`session_begin_nested`.
972 :return: the :class:`.SessionTransaction` object. Note that
973 :class:`.SessionTransaction` acts as a context manager, allowing
974 :meth:`.Session.begin_nested` to be used in a "with" block.
975 See :ref:`session_begin_nested` for a usage example.
977 .. seealso::
979 :ref:`session_begin_nested`
981 :ref:`pysqlite_serializable` - special workarounds required
982 with the SQLite driver in order for SAVEPOINT to work
983 correctly.
985 """
986 return self.begin(nested=True)
988 def rollback(self):
989 """Rollback the current transaction in progress.
991 If no transaction is in progress, this method is a pass-through.
993 This method rolls back the current transaction or nested transaction
994 regardless of subtransactions being in effect. All subtransactions up
995 to the first real transaction are closed. Subtransactions occur when
996 :meth:`.begin` is called multiple times.
998 .. seealso::
1000 :ref:`session_rollback`
1002 """
1003 if self.transaction is None:
1004 pass
1005 else:
1006 self.transaction.rollback()
1008 def commit(self):
1009 """Flush pending changes and commit the current transaction.
1011 If no transaction is in progress, this method raises an
1012 :exc:`~sqlalchemy.exc.InvalidRequestError`.
1014 By default, the :class:`.Session` also expires all database
1015 loaded state on all ORM-managed attributes after transaction commit.
1016 This so that subsequent operations load the most recent
1017 data from the database. This behavior can be disabled using
1018 the ``expire_on_commit=False`` option to :class:`.sessionmaker` or
1019 the :class:`.Session` constructor.
1021 If a subtransaction is in effect (which occurs when begin() is called
1022 multiple times), the subtransaction will be closed, and the next call
1023 to ``commit()`` will operate on the enclosing transaction.
1025 When using the :class:`.Session` in its default mode of
1026 ``autocommit=False``, a new transaction will
1027 be begun immediately after the commit, but note that the newly begun
1028 transaction does *not* use any connection resources until the first
1029 SQL is actually emitted.
1031 .. seealso::
1033 :ref:`session_committing`
1035 """
1036 if self.transaction is None:
1037 if not self.autocommit:
1038 self.begin()
1039 else:
1040 raise sa_exc.InvalidRequestError("No transaction is begun.")
1042 self.transaction.commit()
1044 def prepare(self):
1045 """Prepare the current transaction in progress for two phase commit.
1047 If no transaction is in progress, this method raises an
1048 :exc:`~sqlalchemy.exc.InvalidRequestError`.
1050 Only root transactions of two phase sessions can be prepared. If the
1051 current transaction is not such, an
1052 :exc:`~sqlalchemy.exc.InvalidRequestError` is raised.
1054 """
1055 if self.transaction is None:
1056 if not self.autocommit:
1057 self.begin()
1058 else:
1059 raise sa_exc.InvalidRequestError("No transaction is begun.")
1061 self.transaction.prepare()
1063 def connection(
1064 self,
1065 mapper=None,
1066 clause=None,
1067 bind=None,
1068 close_with_result=False,
1069 execution_options=None,
1070 **kw
1071 ):
1072 r"""Return a :class:`_engine.Connection` object corresponding to this
1073 :class:`.Session` object's transactional state.
1075 If this :class:`.Session` is configured with ``autocommit=False``,
1076 either the :class:`_engine.Connection` corresponding to the current
1077 transaction is returned, or if no transaction is in progress, a new
1078 one is begun and the :class:`_engine.Connection`
1079 returned (note that no
1080 transactional state is established with the DBAPI until the first
1081 SQL statement is emitted).
1083 Alternatively, if this :class:`.Session` is configured with
1084 ``autocommit=True``, an ad-hoc :class:`_engine.Connection` is returned
1085 using :meth:`_engine.Engine.connect` on the underlying
1086 :class:`_engine.Engine`.
1088 Ambiguity in multi-bind or unbound :class:`.Session` objects can be
1089 resolved through any of the optional keyword arguments. This
1090 ultimately makes usage of the :meth:`.get_bind` method for resolution.
1092 :param bind:
1093 Optional :class:`_engine.Engine` to be used as the bind. If
1094 this engine is already involved in an ongoing transaction,
1095 that connection will be used. This argument takes precedence
1096 over ``mapper``, ``clause``.
1098 :param mapper:
1099 Optional :func:`.mapper` mapped class, used to identify
1100 the appropriate bind. This argument takes precedence over
1101 ``clause``.
1103 :param clause:
1104 A :class:`_expression.ClauseElement` (i.e.
1105 :func:`_expression.select`,
1106 :func:`_expression.text`,
1107 etc.) which will be used to locate a bind, if a bind
1108 cannot otherwise be identified.
1110 :param close_with_result: Passed to :meth:`_engine.Engine.connect`,
1111 indicating the :class:`_engine.Connection` should be considered
1112 "single use", automatically closing when the first result set is
1113 closed. This flag only has an effect if this :class:`.Session` is
1114 configured with ``autocommit=True`` and does not already have a
1115 transaction in progress.
1117 :param execution_options: a dictionary of execution options that will
1118 be passed to :meth:`_engine.Connection.execution_options`, **when the
1119 connection is first procured only**. If the connection is already
1120 present within the :class:`.Session`, a warning is emitted and
1121 the arguments are ignored.
1123 .. versionadded:: 0.9.9
1125 .. seealso::
1127 :ref:`session_transaction_isolation`
1129 :param \**kw:
1130 Additional keyword arguments are sent to :meth:`get_bind()`,
1131 allowing additional arguments to be passed to custom
1132 implementations of :meth:`get_bind`.
1134 """
1135 if bind is None:
1136 bind = self.get_bind(mapper, clause=clause, **kw)
1138 return self._connection_for_bind(
1139 bind,
1140 close_with_result=close_with_result,
1141 execution_options=execution_options,
1142 )
1144 def _connection_for_bind(self, engine, execution_options=None, **kw):
1145 if self.transaction is not None:
1146 return self.transaction._connection_for_bind(
1147 engine, execution_options
1148 )
1149 else:
1150 conn = engine._contextual_connect(**kw)
1151 if execution_options:
1152 conn = conn.execution_options(**execution_options)
1153 return conn
1155 def execute(self, clause, params=None, mapper=None, bind=None, **kw):
1156 r"""Execute a SQL expression construct or string statement within
1157 the current transaction.
1159 Returns a :class:`_engine.ResultProxy` representing
1160 results of the statement execution, in the same manner as that of an
1161 :class:`_engine.Engine` or
1162 :class:`_engine.Connection`.
1164 E.g.::
1166 result = session.execute(
1167 user_table.select().where(user_table.c.id == 5)
1168 )
1170 :meth:`~.Session.execute` accepts any executable clause construct,
1171 such as :func:`_expression.select`,
1172 :func:`_expression.insert`,
1173 :func:`_expression.update`,
1174 :func:`_expression.delete`, and
1175 :func:`_expression.text`. Plain SQL strings can be passed
1176 as well, which in the case of :meth:`.Session.execute` only
1177 will be interpreted the same as if it were passed via a
1178 :func:`_expression.text` construct. That is, the following usage::
1180 result = session.execute(
1181 "SELECT * FROM user WHERE id=:param",
1182 {"param":5}
1183 )
1185 is equivalent to::
1187 from sqlalchemy import text
1188 result = session.execute(
1189 text("SELECT * FROM user WHERE id=:param"),
1190 {"param":5}
1191 )
1193 The second positional argument to :meth:`.Session.execute` is an
1194 optional parameter set. Similar to that of
1195 :meth:`_engine.Connection.execute`, whether this is passed as a single
1196 dictionary, or a sequence of dictionaries, determines whether the DBAPI
1197 cursor's ``execute()`` or ``executemany()`` is used to execute the
1198 statement. An INSERT construct may be invoked for a single row::
1200 result = session.execute(
1201 users.insert(), {"id": 7, "name": "somename"})
1203 or for multiple rows::
1205 result = session.execute(users.insert(), [
1206 {"id": 7, "name": "somename7"},
1207 {"id": 8, "name": "somename8"},
1208 {"id": 9, "name": "somename9"}
1209 ])
1211 The statement is executed within the current transactional context of
1212 this :class:`.Session`. The :class:`_engine.Connection`
1213 which is used
1214 to execute the statement can also be acquired directly by
1215 calling the :meth:`.Session.connection` method. Both methods use
1216 a rule-based resolution scheme in order to determine the
1217 :class:`_engine.Connection`,
1218 which in the average case is derived directly
1219 from the "bind" of the :class:`.Session` itself, and in other cases
1220 can be based on the :func:`.mapper`
1221 and :class:`_schema.Table` objects passed to the method; see the
1222 documentation for :meth:`.Session.get_bind` for a full description of
1223 this scheme.
1225 The :meth:`.Session.execute` method does *not* invoke autoflush.
1227 The :class:`_engine.ResultProxy` returned by the
1228 :meth:`.Session.execute`
1229 method is returned with the "close_with_result" flag set to true;
1230 the significance of this flag is that if this :class:`.Session` is
1231 autocommitting and does not have a transaction-dedicated
1232 :class:`_engine.Connection` available, a temporary
1233 :class:`_engine.Connection` is
1234 established for the statement execution, which is closed (meaning,
1235 returned to the connection pool) when the :class:`_engine.ResultProxy`
1236 has
1237 consumed all available data. This applies *only* when the
1238 :class:`.Session` is configured with autocommit=True and no
1239 transaction has been started.
1241 :param clause:
1242 An executable statement (i.e. an :class:`.Executable` expression
1243 such as :func:`_expression.select`) or string SQL statement
1244 to be executed.
1246 :param params:
1247 Optional dictionary, or list of dictionaries, containing
1248 bound parameter values. If a single dictionary, single-row
1249 execution occurs; if a list of dictionaries, an
1250 "executemany" will be invoked. The keys in each dictionary
1251 must correspond to parameter names present in the statement.
1253 :param mapper:
1254 Optional :func:`.mapper` or mapped class, used to identify
1255 the appropriate bind. This argument takes precedence over
1256 ``clause`` when locating a bind. See :meth:`.Session.get_bind`
1257 for more details.
1259 :param bind:
1260 Optional :class:`_engine.Engine` to be used as the bind. If
1261 this engine is already involved in an ongoing transaction,
1262 that connection will be used. This argument takes
1263 precedence over ``mapper`` and ``clause`` when locating
1264 a bind.
1266 :param \**kw:
1267 Additional keyword arguments are sent to :meth:`.Session.get_bind()`
1268 to allow extensibility of "bind" schemes.
1270 .. seealso::
1272 :ref:`sqlexpression_toplevel` - Tutorial on using Core SQL
1273 constructs.
1275 :ref:`connections_toplevel` - Further information on direct
1276 statement execution.
1278 :meth:`_engine.Connection.execute`
1279 - core level statement execution
1280 method, which is :meth:`.Session.execute` ultimately uses
1281 in order to execute the statement.
1283 """
1284 clause = expression._literal_as_text(
1285 clause, allow_coercion_to_text=True
1286 )
1288 if bind is None:
1289 bind = self.get_bind(mapper, clause=clause, **kw)
1291 return self._connection_for_bind(bind, close_with_result=True).execute(
1292 clause, params or {}
1293 )
1295 def scalar(self, clause, params=None, mapper=None, bind=None, **kw):
1296 """Like :meth:`~.Session.execute` but return a scalar result."""
1298 return self.execute(
1299 clause, params=params, mapper=mapper, bind=bind, **kw
1300 ).scalar()
1302 def close(self):
1303 """Close this Session.
1305 This clears all items and ends any transaction in progress.
1307 If this session were created with ``autocommit=False``, a new
1308 transaction is immediately begun. Note that this new transaction does
1309 not use any connection resources until they are first needed.
1311 """
1312 self._close_impl(invalidate=False)
1314 def invalidate(self):
1315 """Close this Session, using connection invalidation.
1317 This is a variant of :meth:`.Session.close` that will additionally
1318 ensure that the :meth:`_engine.Connection.invalidate`
1319 method will be called
1320 on all :class:`_engine.Connection` objects. This can be called when
1321 the database is known to be in a state where the connections are
1322 no longer safe to be used.
1324 E.g.::
1326 try:
1327 sess = Session()
1328 sess.add(User())
1329 sess.commit()
1330 except gevent.Timeout:
1331 sess.invalidate()
1332 raise
1333 except:
1334 sess.rollback()
1335 raise
1337 This clears all items and ends any transaction in progress.
1339 If this session were created with ``autocommit=False``, a new
1340 transaction is immediately begun. Note that this new transaction does
1341 not use any connection resources until they are first needed.
1343 .. versionadded:: 0.9.9
1345 """
1346 self._close_impl(invalidate=True)
1348 def _close_impl(self, invalidate):
1349 self.expunge_all()
1350 if self.transaction is not None:
1351 for transaction in self.transaction._iterate_self_and_parents():
1352 transaction.close(invalidate)
1354 def expunge_all(self):
1355 """Remove all object instances from this ``Session``.
1357 This is equivalent to calling ``expunge(obj)`` on all objects in this
1358 ``Session``.
1360 """
1362 all_states = self.identity_map.all_states() + list(self._new)
1363 self.identity_map = self._identity_cls()
1364 self._new = {}
1365 self._deleted = {}
1367 statelib.InstanceState._detach_states(all_states, self)
1369 def _add_bind(self, key, bind):
1370 try:
1371 insp = inspect(key)
1372 except sa_exc.NoInspectionAvailable as err:
1373 if not isinstance(key, type):
1374 util.raise_(
1375 sa_exc.ArgumentError(
1376 "Not an acceptable bind target: %s" % key
1377 ),
1378 replace_context=err,
1379 )
1380 else:
1381 self.__binds[key] = bind
1382 else:
1383 if insp.is_selectable:
1384 self.__binds[insp] = bind
1385 elif insp.is_mapper:
1386 self.__binds[insp.class_] = bind
1387 for selectable in insp._all_tables:
1388 self.__binds[selectable] = bind
1389 else:
1390 raise sa_exc.ArgumentError(
1391 "Not an acceptable bind target: %s" % key
1392 )
1394 def bind_mapper(self, mapper, bind):
1395 """Associate a :class:`_orm.Mapper` or arbitrary Python class with a
1396 "bind", e.g. an :class:`_engine.Engine` or :class:`_engine.Connection`
1397 .
1399 The given entity is added to a lookup used by the
1400 :meth:`.Session.get_bind` method.
1402 :param mapper: a :class:`_orm.Mapper` object,
1403 or an instance of a mapped
1404 class, or any Python class that is the base of a set of mapped
1405 classes.
1407 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection`
1408 object.
1410 .. seealso::
1412 :ref:`session_partitioning`
1414 :paramref:`.Session.binds`
1416 :meth:`.Session.bind_table`
1419 """
1420 self._add_bind(mapper, bind)
1422 def bind_table(self, table, bind):
1423 """Associate a :class:`_schema.Table` with a "bind", e.g. an
1424 :class:`_engine.Engine`
1425 or :class:`_engine.Connection`.
1427 The given :class:`_schema.Table` is added to a lookup used by the
1428 :meth:`.Session.get_bind` method.
1430 :param table: a :class:`_schema.Table` object,
1431 which is typically the target
1432 of an ORM mapping, or is present within a selectable that is
1433 mapped.
1435 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection`
1436 object.
1438 .. seealso::
1440 :ref:`session_partitioning`
1442 :paramref:`.Session.binds`
1444 :meth:`.Session.bind_mapper`
1447 """
1448 self._add_bind(table, bind)
1450 def get_bind(self, mapper=None, clause=None):
1451 """Return a "bind" to which this :class:`.Session` is bound.
1453 The "bind" is usually an instance of :class:`_engine.Engine`,
1454 except in the case where the :class:`.Session` has been
1455 explicitly bound directly to a :class:`_engine.Connection`.
1457 For a multiply-bound or unbound :class:`.Session`, the
1458 ``mapper`` or ``clause`` arguments are used to determine the
1459 appropriate bind to return.
1461 Note that the "mapper" argument is usually present
1462 when :meth:`.Session.get_bind` is called via an ORM
1463 operation such as a :meth:`.Session.query`, each
1464 individual INSERT/UPDATE/DELETE operation within a
1465 :meth:`.Session.flush`, call, etc.
1467 The order of resolution is:
1469 1. if mapper given and session.binds is present,
1470 locate a bind based first on the mapper in use, then
1471 on the mapped class in use, then on any base classes that are
1472 present in the ``__mro__`` of the mapped class, from more specific
1473 superclasses to more general.
1474 2. if clause given and session.binds is present,
1475 locate a bind based on :class:`_schema.Table` objects
1476 found in the given clause present in session.binds.
1477 3. if session.bind is present, return that.
1478 4. if clause given, attempt to return a bind
1479 linked to the :class:`_schema.MetaData` ultimately
1480 associated with the clause.
1481 5. if mapper given, attempt to return a bind
1482 linked to the :class:`_schema.MetaData` ultimately
1483 associated with the :class:`_schema.Table` or other
1484 selectable to which the mapper is mapped.
1485 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError`
1486 is raised.
1488 Note that the :meth:`.Session.get_bind` method can be overridden on
1489 a user-defined subclass of :class:`.Session` to provide any kind
1490 of bind resolution scheme. See the example at
1491 :ref:`session_custom_partitioning`.
1493 :param mapper:
1494 Optional :func:`.mapper` mapped class or instance of
1495 :class:`_orm.Mapper`. The bind can be derived from a
1496 :class:`_orm.Mapper`
1497 first by consulting the "binds" map associated with this
1498 :class:`.Session`, and secondly by consulting the
1499 :class:`_schema.MetaData`
1500 associated with the :class:`_schema.Table` to which the
1501 :class:`_orm.Mapper`
1502 is mapped for a bind.
1504 :param clause:
1505 A :class:`_expression.ClauseElement` (i.e.
1506 :func:`_expression.select`,
1507 :func:`_expression.text`,
1508 etc.). If the ``mapper`` argument is not present or could not
1509 produce a bind, the given expression construct will be searched
1510 for a bound element, typically a :class:`_schema.Table`
1511 associated with
1512 bound :class:`_schema.MetaData`.
1514 .. seealso::
1516 :ref:`session_partitioning`
1518 :paramref:`.Session.binds`
1520 :meth:`.Session.bind_mapper`
1522 :meth:`.Session.bind_table`
1524 """
1526 if mapper is clause is None:
1527 if self.bind:
1528 return self.bind
1529 else:
1530 raise sa_exc.UnboundExecutionError(
1531 "This session is not bound to a single Engine or "
1532 "Connection, and no context was provided to locate "
1533 "a binding."
1534 )
1536 if mapper is not None:
1537 try:
1538 mapper = inspect(mapper)
1539 except sa_exc.NoInspectionAvailable as err:
1540 if isinstance(mapper, type):
1541 util.raise_(
1542 exc.UnmappedClassError(mapper), replace_context=err,
1543 )
1544 else:
1545 raise
1547 if self.__binds:
1548 if mapper:
1549 for cls in mapper.class_.__mro__:
1550 if cls in self.__binds:
1551 return self.__binds[cls]
1552 if clause is None:
1553 clause = mapper.persist_selectable
1555 if clause is not None:
1556 for t in sql_util.find_tables(clause, include_crud=True):
1557 if t in self.__binds:
1558 return self.__binds[t]
1560 if self.bind:
1561 return self.bind
1563 if isinstance(clause, sql.expression.ClauseElement) and clause.bind:
1564 return clause.bind
1566 if mapper and mapper.persist_selectable.bind:
1567 return mapper.persist_selectable.bind
1569 context = []
1570 if mapper is not None:
1571 context.append("mapper %s" % mapper)
1572 if clause is not None:
1573 context.append("SQL expression")
1575 raise sa_exc.UnboundExecutionError(
1576 "Could not locate a bind configured on %s or this Session"
1577 % (", ".join(context))
1578 )
1580 def query(self, *entities, **kwargs):
1581 """Return a new :class:`_query.Query` object corresponding to this
1582 :class:`.Session`."""
1584 return self._query_cls(entities, self, **kwargs)
1586 @property
1587 @util.contextmanager
1588 def no_autoflush(self):
1589 """Return a context manager that disables autoflush.
1591 e.g.::
1593 with session.no_autoflush:
1595 some_object = SomeClass()
1596 session.add(some_object)
1597 # won't autoflush
1598 some_object.related_thing = session.query(SomeRelated).first()
1600 Operations that proceed within the ``with:`` block
1601 will not be subject to flushes occurring upon query
1602 access. This is useful when initializing a series
1603 of objects which involve existing database queries,
1604 where the uncompleted object should not yet be flushed.
1606 """
1607 autoflush = self.autoflush
1608 self.autoflush = False
1609 try:
1610 yield self
1611 finally:
1612 self.autoflush = autoflush
1614 def _autoflush(self):
1615 if self.autoflush and not self._flushing:
1616 try:
1617 self.flush()
1618 except sa_exc.StatementError as e:
1619 # note we are reraising StatementError as opposed to
1620 # raising FlushError with "chaining" to remain compatible
1621 # with code that catches StatementError, IntegrityError,
1622 # etc.
1623 e.add_detail(
1624 "raised as a result of Query-invoked autoflush; "
1625 "consider using a session.no_autoflush block if this "
1626 "flush is occurring prematurely"
1627 )
1628 util.raise_(e, with_traceback=sys.exc_info()[2])
1630 def refresh(
1631 self,
1632 instance,
1633 attribute_names=None,
1634 with_for_update=None,
1635 lockmode=None,
1636 ):
1637 """Expire and refresh the attributes on the given instance.
1639 A query will be issued to the database and all attributes will be
1640 refreshed with their current database value.
1642 Lazy-loaded relational attributes will remain lazily loaded, so that
1643 the instance-wide refresh operation will be followed immediately by
1644 the lazy load of that attribute.
1646 Eagerly-loaded relational attributes will eagerly load within the
1647 single refresh operation.
1649 Note that a highly isolated transaction will return the same values as
1650 were previously read in that same transaction, regardless of changes
1651 in database state outside of that transaction - usage of
1652 :meth:`~Session.refresh` usually only makes sense if non-ORM SQL
1653 statement were emitted in the ongoing transaction, or if autocommit
1654 mode is turned on.
1656 :param attribute_names: optional. An iterable collection of
1657 string attribute names indicating a subset of attributes to
1658 be refreshed.
1660 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE
1661 should be used, or may be a dictionary containing flags to
1662 indicate a more specific set of FOR UPDATE flags for the SELECT;
1663 flags should match the parameters of
1664 :meth:`_query.Query.with_for_update`.
1665 Supersedes the :paramref:`.Session.refresh.lockmode` parameter.
1667 .. versionadded:: 1.2
1669 :param lockmode: Passed to the :class:`~sqlalchemy.orm.query.Query`
1670 as used by :meth:`~sqlalchemy.orm.query.Query.with_lockmode`.
1671 Superseded by :paramref:`.Session.refresh.with_for_update`.
1673 .. seealso::
1675 :ref:`session_expire` - introductory material
1677 :meth:`.Session.expire`
1679 :meth:`.Session.expire_all`
1681 """
1682 try:
1683 state = attributes.instance_state(instance)
1684 except exc.NO_STATE as err:
1685 util.raise_(
1686 exc.UnmappedInstanceError(instance), replace_context=err,
1687 )
1689 self._expire_state(state, attribute_names)
1691 if with_for_update == {}:
1692 raise sa_exc.ArgumentError(
1693 "with_for_update should be the boolean value "
1694 "True, or a dictionary with options. "
1695 "A blank dictionary is ambiguous."
1696 )
1698 if lockmode:
1699 with_for_update = query.LockmodeArg.parse_legacy_query(lockmode)
1700 elif with_for_update is not None:
1701 if with_for_update is True:
1702 with_for_update = query.LockmodeArg()
1703 elif with_for_update:
1704 with_for_update = query.LockmodeArg(**with_for_update)
1705 else:
1706 with_for_update = None
1708 if (
1709 loading.load_on_ident(
1710 self.query(object_mapper(instance)),
1711 state.key,
1712 refresh_state=state,
1713 with_for_update=with_for_update,
1714 only_load_props=attribute_names,
1715 )
1716 is None
1717 ):
1718 raise sa_exc.InvalidRequestError(
1719 "Could not refresh instance '%s'" % instance_str(instance)
1720 )
1722 def expire_all(self):
1723 """Expires all persistent instances within this Session.
1725 When any attributes on a persistent instance is next accessed,
1726 a query will be issued using the
1727 :class:`.Session` object's current transactional context in order to
1728 load all expired attributes for the given instance. Note that
1729 a highly isolated transaction will return the same values as were
1730 previously read in that same transaction, regardless of changes
1731 in database state outside of that transaction.
1733 To expire individual objects and individual attributes
1734 on those objects, use :meth:`Session.expire`.
1736 The :class:`.Session` object's default behavior is to
1737 expire all state whenever the :meth:`Session.rollback`
1738 or :meth:`Session.commit` methods are called, so that new
1739 state can be loaded for the new transaction. For this reason,
1740 calling :meth:`Session.expire_all` should not be needed when
1741 autocommit is ``False``, assuming the transaction is isolated.
1743 .. seealso::
1745 :ref:`session_expire` - introductory material
1747 :meth:`.Session.expire`
1749 :meth:`.Session.refresh`
1751 """
1752 for state in self.identity_map.all_states():
1753 state._expire(state.dict, self.identity_map._modified)
1755 def expire(self, instance, attribute_names=None):
1756 """Expire the attributes on an instance.
1758 Marks the attributes of an instance as out of date. When an expired
1759 attribute is next accessed, a query will be issued to the
1760 :class:`.Session` object's current transactional context in order to
1761 load all expired attributes for the given instance. Note that
1762 a highly isolated transaction will return the same values as were
1763 previously read in that same transaction, regardless of changes
1764 in database state outside of that transaction.
1766 To expire all objects in the :class:`.Session` simultaneously,
1767 use :meth:`Session.expire_all`.
1769 The :class:`.Session` object's default behavior is to
1770 expire all state whenever the :meth:`Session.rollback`
1771 or :meth:`Session.commit` methods are called, so that new
1772 state can be loaded for the new transaction. For this reason,
1773 calling :meth:`Session.expire` only makes sense for the specific
1774 case that a non-ORM SQL statement was emitted in the current
1775 transaction.
1777 :param instance: The instance to be refreshed.
1778 :param attribute_names: optional list of string attribute names
1779 indicating a subset of attributes to be expired.
1781 .. seealso::
1783 :ref:`session_expire` - introductory material
1785 :meth:`.Session.expire`
1787 :meth:`.Session.refresh`
1789 """
1790 try:
1791 state = attributes.instance_state(instance)
1792 except exc.NO_STATE as err:
1793 util.raise_(
1794 exc.UnmappedInstanceError(instance), replace_context=err,
1795 )
1796 self._expire_state(state, attribute_names)
1798 def _expire_state(self, state, attribute_names):
1799 self._validate_persistent(state)
1800 if attribute_names:
1801 state._expire_attributes(state.dict, attribute_names)
1802 else:
1803 # pre-fetch the full cascade since the expire is going to
1804 # remove associations
1805 cascaded = list(
1806 state.manager.mapper.cascade_iterator("refresh-expire", state)
1807 )
1808 self._conditional_expire(state)
1809 for o, m, st_, dct_ in cascaded:
1810 self._conditional_expire(st_)
1812 def _conditional_expire(self, state):
1813 """Expire a state if persistent, else expunge if pending"""
1815 if state.key:
1816 state._expire(state.dict, self.identity_map._modified)
1817 elif state in self._new:
1818 self._new.pop(state)
1819 state._detach(self)
1821 @util.deprecated(
1822 "0.7",
1823 "The :meth:`.Session.prune` method is deprecated along with "
1824 ":paramref:`.Session.weak_identity_map`. This method will be "
1825 "removed in a future release.",
1826 )
1827 def prune(self):
1828 """Remove unreferenced instances cached in the identity map.
1830 Note that this method is only meaningful if "weak_identity_map" is set
1831 to False. The default weak identity map is self-pruning.
1833 Removes any object in this Session's identity map that is not
1834 referenced in user code, modified, new or scheduled for deletion.
1835 Returns the number of objects pruned.
1837 """
1838 return self.identity_map.prune()
1840 def expunge(self, instance):
1841 """Remove the `instance` from this ``Session``.
1843 This will free all internal references to the instance. Cascading
1844 will be applied according to the *expunge* cascade rule.
1846 """
1847 try:
1848 state = attributes.instance_state(instance)
1849 except exc.NO_STATE as err:
1850 util.raise_(
1851 exc.UnmappedInstanceError(instance), replace_context=err,
1852 )
1853 if state.session_id is not self.hash_key:
1854 raise sa_exc.InvalidRequestError(
1855 "Instance %s is not present in this Session" % state_str(state)
1856 )
1858 cascaded = list(
1859 state.manager.mapper.cascade_iterator("expunge", state)
1860 )
1861 self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded])
1863 def _expunge_states(self, states, to_transient=False):
1864 for state in states:
1865 if state in self._new:
1866 self._new.pop(state)
1867 elif self.identity_map.contains_state(state):
1868 self.identity_map.safe_discard(state)
1869 self._deleted.pop(state, None)
1870 elif self.transaction:
1871 # state is "detached" from being deleted, but still present
1872 # in the transaction snapshot
1873 self.transaction._deleted.pop(state, None)
1874 statelib.InstanceState._detach_states(
1875 states, self, to_transient=to_transient
1876 )
1878 def _register_persistent(self, states):
1879 """Register all persistent objects from a flush.
1881 This is used both for pending objects moving to the persistent
1882 state as well as already persistent objects.
1884 """
1886 pending_to_persistent = self.dispatch.pending_to_persistent or None
1887 for state in states:
1888 mapper = _state_mapper(state)
1890 # prevent against last minute dereferences of the object
1891 obj = state.obj()
1892 if obj is not None:
1894 instance_key = mapper._identity_key_from_state(state)
1896 if (
1897 _none_set.intersection(instance_key[1])
1898 and not mapper.allow_partial_pks
1899 or _none_set.issuperset(instance_key[1])
1900 ):
1901 raise exc.FlushError(
1902 "Instance %s has a NULL identity key. If this is an "
1903 "auto-generated value, check that the database table "
1904 "allows generation of new primary key values, and "
1905 "that the mapped Column object is configured to "
1906 "expect these generated values. Ensure also that "
1907 "this flush() is not occurring at an inappropriate "
1908 "time, such as within a load() event."
1909 % state_str(state)
1910 )
1912 if state.key is None:
1913 state.key = instance_key
1914 elif state.key != instance_key:
1915 # primary key switch. use safe_discard() in case another
1916 # state has already replaced this one in the identity
1917 # map (see test/orm/test_naturalpks.py ReversePKsTest)
1918 self.identity_map.safe_discard(state)
1919 if state in self.transaction._key_switches:
1920 orig_key = self.transaction._key_switches[state][0]
1921 else:
1922 orig_key = state.key
1923 self.transaction._key_switches[state] = (
1924 orig_key,
1925 instance_key,
1926 )
1927 state.key = instance_key
1929 # there can be an existing state in the identity map
1930 # that is replaced when the primary keys of two instances
1931 # are swapped; see test/orm/test_naturalpks.py -> test_reverse
1932 old = self.identity_map.replace(state)
1933 if (
1934 old is not None
1935 and mapper._identity_key_from_state(old) == instance_key
1936 and old.obj() is not None
1937 ):
1938 util.warn(
1939 "Identity map already had an identity for %s, "
1940 "replacing it with newly flushed object. Are there "
1941 "load operations occurring inside of an event handler "
1942 "within the flush?" % (instance_key,)
1943 )
1944 state._orphaned_outside_of_session = False
1946 statelib.InstanceState._commit_all_states(
1947 ((state, state.dict) for state in states), self.identity_map
1948 )
1950 self._register_altered(states)
1952 if pending_to_persistent is not None:
1953 for state in states.intersection(self._new):
1954 pending_to_persistent(self, state)
1956 # remove from new last, might be the last strong ref
1957 for state in set(states).intersection(self._new):
1958 self._new.pop(state)
1960 def _register_altered(self, states):
1961 if self._enable_transaction_accounting and self.transaction:
1962 for state in states:
1963 if state in self._new:
1964 self.transaction._new[state] = True
1965 else:
1966 self.transaction._dirty[state] = True
1968 def _remove_newly_deleted(self, states):
1969 persistent_to_deleted = self.dispatch.persistent_to_deleted or None
1970 for state in states:
1971 if self._enable_transaction_accounting and self.transaction:
1972 self.transaction._deleted[state] = True
1974 if persistent_to_deleted is not None:
1975 # get a strong reference before we pop out of
1976 # self._deleted
1977 obj = state.obj() # noqa
1979 self.identity_map.safe_discard(state)
1980 self._deleted.pop(state, None)
1981 state._deleted = True
1982 # can't call state._detach() here, because this state
1983 # is still in the transaction snapshot and needs to be
1984 # tracked as part of that
1985 if persistent_to_deleted is not None:
1986 persistent_to_deleted(self, state)
1988 def add(self, instance, _warn=True):
1989 """Place an object in the ``Session``.
1991 Its state will be persisted to the database on the next flush
1992 operation.
1994 Repeated calls to ``add()`` will be ignored. The opposite of ``add()``
1995 is ``expunge()``.
1997 """
1998 if _warn and self._warn_on_events:
1999 self._flush_warning("Session.add()")
2001 try:
2002 state = attributes.instance_state(instance)
2003 except exc.NO_STATE as err:
2004 util.raise_(
2005 exc.UnmappedInstanceError(instance), replace_context=err,
2006 )
2008 self._save_or_update_state(state)
2010 def add_all(self, instances):
2011 """Add the given collection of instances to this ``Session``."""
2013 if self._warn_on_events:
2014 self._flush_warning("Session.add_all()")
2016 for instance in instances:
2017 self.add(instance, _warn=False)
2019 def _save_or_update_state(self, state):
2020 state._orphaned_outside_of_session = False
2021 self._save_or_update_impl(state)
2023 mapper = _state_mapper(state)
2024 for o, m, st_, dct_ in mapper.cascade_iterator(
2025 "save-update", state, halt_on=self._contains_state
2026 ):
2027 self._save_or_update_impl(st_)
2029 def delete(self, instance):
2030 """Mark an instance as deleted.
2032 The database delete operation occurs upon ``flush()``.
2034 """
2035 if self._warn_on_events:
2036 self._flush_warning("Session.delete()")
2038 try:
2039 state = attributes.instance_state(instance)
2040 except exc.NO_STATE as err:
2041 util.raise_(
2042 exc.UnmappedInstanceError(instance), replace_context=err,
2043 )
2045 self._delete_impl(state, instance, head=True)
2047 def _delete_impl(self, state, obj, head):
2049 if state.key is None:
2050 if head:
2051 raise sa_exc.InvalidRequestError(
2052 "Instance '%s' is not persisted" % state_str(state)
2053 )
2054 else:
2055 return
2057 to_attach = self._before_attach(state, obj)
2059 if state in self._deleted:
2060 return
2062 self.identity_map.add(state)
2064 if to_attach:
2065 self._after_attach(state, obj)
2067 if head:
2068 # grab the cascades before adding the item to the deleted list
2069 # so that autoflush does not delete the item
2070 # the strong reference to the instance itself is significant here
2071 cascade_states = list(
2072 state.manager.mapper.cascade_iterator("delete", state)
2073 )
2075 self._deleted[state] = obj
2077 if head:
2078 for o, m, st_, dct_ in cascade_states:
2079 self._delete_impl(st_, o, False)
2081 def merge(self, instance, load=True):
2082 """Copy the state of a given instance into a corresponding instance
2083 within this :class:`.Session`.
2085 :meth:`.Session.merge` examines the primary key attributes of the
2086 source instance, and attempts to reconcile it with an instance of the
2087 same primary key in the session. If not found locally, it attempts
2088 to load the object from the database based on primary key, and if
2089 none can be located, creates a new instance. The state of each
2090 attribute on the source instance is then copied to the target
2091 instance. The resulting target instance is then returned by the
2092 method; the original source instance is left unmodified, and
2093 un-associated with the :class:`.Session` if not already.
2095 This operation cascades to associated instances if the association is
2096 mapped with ``cascade="merge"``.
2098 See :ref:`unitofwork_merging` for a detailed discussion of merging.
2100 .. versionchanged:: 1.1 - :meth:`.Session.merge` will now reconcile
2101 pending objects with overlapping primary keys in the same way
2102 as persistent. See :ref:`change_3601` for discussion.
2104 :param instance: Instance to be merged.
2105 :param load: Boolean, when False, :meth:`.merge` switches into
2106 a "high performance" mode which causes it to forego emitting history
2107 events as well as all database access. This flag is used for
2108 cases such as transferring graphs of objects into a :class:`.Session`
2109 from a second level cache, or to transfer just-loaded objects
2110 into the :class:`.Session` owned by a worker thread or process
2111 without re-querying the database.
2113 The ``load=False`` use case adds the caveat that the given
2114 object has to be in a "clean" state, that is, has no pending changes
2115 to be flushed - even if the incoming object is detached from any
2116 :class:`.Session`. This is so that when
2117 the merge operation populates local attributes and
2118 cascades to related objects and
2119 collections, the values can be "stamped" onto the
2120 target object as is, without generating any history or attribute
2121 events, and without the need to reconcile the incoming data with
2122 any existing related objects or collections that might not
2123 be loaded. The resulting objects from ``load=False`` are always
2124 produced as "clean", so it is only appropriate that the given objects
2125 should be "clean" as well, else this suggests a mis-use of the
2126 method.
2129 .. seealso::
2131 :func:`.make_transient_to_detached` - provides for an alternative
2132 means of "merging" a single object into the :class:`.Session`
2134 """
2136 if self._warn_on_events:
2137 self._flush_warning("Session.merge()")
2139 _recursive = {}
2140 _resolve_conflict_map = {}
2142 if load:
2143 # flush current contents if we expect to load data
2144 self._autoflush()
2146 object_mapper(instance) # verify mapped
2147 autoflush = self.autoflush
2148 try:
2149 self.autoflush = False
2150 return self._merge(
2151 attributes.instance_state(instance),
2152 attributes.instance_dict(instance),
2153 load=load,
2154 _recursive=_recursive,
2155 _resolve_conflict_map=_resolve_conflict_map,
2156 )
2157 finally:
2158 self.autoflush = autoflush
2160 def _merge(
2161 self,
2162 state,
2163 state_dict,
2164 load=True,
2165 _recursive=None,
2166 _resolve_conflict_map=None,
2167 ):
2168 mapper = _state_mapper(state)
2169 if state in _recursive:
2170 return _recursive[state]
2172 new_instance = False
2173 key = state.key
2175 if key is None:
2176 if state in self._new:
2177 util.warn(
2178 "Instance %s is already pending in this Session yet is "
2179 "being merged again; this is probably not what you want "
2180 "to do" % state_str(state)
2181 )
2183 if not load:
2184 raise sa_exc.InvalidRequestError(
2185 "merge() with load=False option does not support "
2186 "objects transient (i.e. unpersisted) objects. flush() "
2187 "all changes on mapped instances before merging with "
2188 "load=False."
2189 )
2190 key = mapper._identity_key_from_state(state)
2191 key_is_persistent = attributes.NEVER_SET not in key[1] and (
2192 not _none_set.intersection(key[1])
2193 or (
2194 mapper.allow_partial_pks
2195 and not _none_set.issuperset(key[1])
2196 )
2197 )
2198 else:
2199 key_is_persistent = True
2201 if key in self.identity_map:
2202 try:
2203 merged = self.identity_map[key]
2204 except KeyError:
2205 # object was GC'ed right as we checked for it
2206 merged = None
2207 else:
2208 merged = None
2210 if merged is None:
2211 if key_is_persistent and key in _resolve_conflict_map:
2212 merged = _resolve_conflict_map[key]
2214 elif not load:
2215 if state.modified:
2216 raise sa_exc.InvalidRequestError(
2217 "merge() with load=False option does not support "
2218 "objects marked as 'dirty'. flush() all changes on "
2219 "mapped instances before merging with load=False."
2220 )
2221 merged = mapper.class_manager.new_instance()
2222 merged_state = attributes.instance_state(merged)
2223 merged_state.key = key
2224 self._update_impl(merged_state)
2225 new_instance = True
2227 elif key_is_persistent:
2228 merged = self.query(mapper.class_).get(key[1])
2230 if merged is None:
2231 merged = mapper.class_manager.new_instance()
2232 merged_state = attributes.instance_state(merged)
2233 merged_dict = attributes.instance_dict(merged)
2234 new_instance = True
2235 self._save_or_update_state(merged_state)
2236 else:
2237 merged_state = attributes.instance_state(merged)
2238 merged_dict = attributes.instance_dict(merged)
2240 _recursive[state] = merged
2241 _resolve_conflict_map[key] = merged
2243 # check that we didn't just pull the exact same
2244 # state out.
2245 if state is not merged_state:
2246 # version check if applicable
2247 if mapper.version_id_col is not None:
2248 existing_version = mapper._get_state_attr_by_column(
2249 state,
2250 state_dict,
2251 mapper.version_id_col,
2252 passive=attributes.PASSIVE_NO_INITIALIZE,
2253 )
2255 merged_version = mapper._get_state_attr_by_column(
2256 merged_state,
2257 merged_dict,
2258 mapper.version_id_col,
2259 passive=attributes.PASSIVE_NO_INITIALIZE,
2260 )
2262 if (
2263 existing_version is not attributes.PASSIVE_NO_RESULT
2264 and merged_version is not attributes.PASSIVE_NO_RESULT
2265 and existing_version != merged_version
2266 ):
2267 raise exc.StaleDataError(
2268 "Version id '%s' on merged state %s "
2269 "does not match existing version '%s'. "
2270 "Leave the version attribute unset when "
2271 "merging to update the most recent version."
2272 % (
2273 existing_version,
2274 state_str(merged_state),
2275 merged_version,
2276 )
2277 )
2279 merged_state.load_path = state.load_path
2280 merged_state.load_options = state.load_options
2282 # since we are copying load_options, we need to copy
2283 # the callables_ that would have been generated by those
2284 # load_options.
2285 # assumes that the callables we put in state.callables_
2286 # are not instance-specific (which they should not be)
2287 merged_state._copy_callables(state)
2289 for prop in mapper.iterate_properties:
2290 prop.merge(
2291 self,
2292 state,
2293 state_dict,
2294 merged_state,
2295 merged_dict,
2296 load,
2297 _recursive,
2298 _resolve_conflict_map,
2299 )
2301 if not load:
2302 # remove any history
2303 merged_state._commit_all(merged_dict, self.identity_map)
2305 if new_instance:
2306 merged_state.manager.dispatch.load(merged_state, None)
2307 return merged
2309 def _validate_persistent(self, state):
2310 if not self.identity_map.contains_state(state):
2311 raise sa_exc.InvalidRequestError(
2312 "Instance '%s' is not persistent within this Session"
2313 % state_str(state)
2314 )
2316 def _save_impl(self, state):
2317 if state.key is not None:
2318 raise sa_exc.InvalidRequestError(
2319 "Object '%s' already has an identity - "
2320 "it can't be registered as pending" % state_str(state)
2321 )
2323 obj = state.obj()
2324 to_attach = self._before_attach(state, obj)
2325 if state not in self._new:
2326 self._new[state] = obj
2327 state.insert_order = len(self._new)
2328 if to_attach:
2329 self._after_attach(state, obj)
2331 def _update_impl(self, state, revert_deletion=False):
2332 if state.key is None:
2333 raise sa_exc.InvalidRequestError(
2334 "Instance '%s' is not persisted" % state_str(state)
2335 )
2337 if state._deleted:
2338 if revert_deletion:
2339 if not state._attached:
2340 return
2341 del state._deleted
2342 else:
2343 raise sa_exc.InvalidRequestError(
2344 "Instance '%s' has been deleted. "
2345 "Use the make_transient() "
2346 "function to send this object back "
2347 "to the transient state." % state_str(state)
2348 )
2350 obj = state.obj()
2352 # check for late gc
2353 if obj is None:
2354 return
2356 to_attach = self._before_attach(state, obj)
2358 self._deleted.pop(state, None)
2359 if revert_deletion:
2360 self.identity_map.replace(state)
2361 else:
2362 self.identity_map.add(state)
2364 if to_attach:
2365 self._after_attach(state, obj)
2366 elif revert_deletion:
2367 self.dispatch.deleted_to_persistent(self, state)
2369 def _save_or_update_impl(self, state):
2370 if state.key is None:
2371 self._save_impl(state)
2372 else:
2373 self._update_impl(state)
2375 def enable_relationship_loading(self, obj):
2376 """Associate an object with this :class:`.Session` for related
2377 object loading.
2379 .. warning::
2381 :meth:`.enable_relationship_loading` exists to serve special
2382 use cases and is not recommended for general use.
2384 Accesses of attributes mapped with :func:`_orm.relationship`
2385 will attempt to load a value from the database using this
2386 :class:`.Session` as the source of connectivity. The values
2387 will be loaded based on foreign key and primary key values
2388 present on this object - if not present, then those relationships
2389 will be unavailable.
2391 The object will be attached to this session, but will
2392 **not** participate in any persistence operations; its state
2393 for almost all purposes will remain either "transient" or
2394 "detached", except for the case of relationship loading.
2396 Also note that backrefs will often not work as expected.
2397 Altering a relationship-bound attribute on the target object
2398 may not fire off a backref event, if the effective value
2399 is what was already loaded from a foreign-key-holding value.
2401 The :meth:`.Session.enable_relationship_loading` method is
2402 similar to the ``load_on_pending`` flag on :func:`_orm.relationship`.
2403 Unlike that flag, :meth:`.Session.enable_relationship_loading` allows
2404 an object to remain transient while still being able to load
2405 related items.
2407 To make a transient object associated with a :class:`.Session`
2408 via :meth:`.Session.enable_relationship_loading` pending, add
2409 it to the :class:`.Session` using :meth:`.Session.add` normally.
2410 If the object instead represents an existing identity in the database,
2411 it should be merged using :meth:`.Session.merge`.
2413 :meth:`.Session.enable_relationship_loading` does not improve
2414 behavior when the ORM is used normally - object references should be
2415 constructed at the object level, not at the foreign key level, so
2416 that they are present in an ordinary way before flush()
2417 proceeds. This method is not intended for general use.
2419 .. seealso::
2421 ``load_on_pending`` at :func:`_orm.relationship` - this flag
2422 allows per-relationship loading of many-to-ones on items that
2423 are pending.
2425 :func:`.make_transient_to_detached` - allows for an object to
2426 be added to a :class:`.Session` without SQL emitted, which then
2427 will unexpire attributes on access.
2429 """
2430 state = attributes.instance_state(obj)
2431 to_attach = self._before_attach(state, obj)
2432 state._load_pending = True
2433 if to_attach:
2434 self._after_attach(state, obj)
2436 def _before_attach(self, state, obj):
2437 if state.session_id == self.hash_key:
2438 return False
2440 if state.session_id and state.session_id in _sessions:
2441 raise sa_exc.InvalidRequestError(
2442 "Object '%s' is already attached to session '%s' "
2443 "(this is '%s')"
2444 % (state_str(state), state.session_id, self.hash_key)
2445 )
2447 self.dispatch.before_attach(self, state)
2449 return True
2451 def _after_attach(self, state, obj):
2452 state.session_id = self.hash_key
2453 if state.modified and state._strong_obj is None:
2454 state._strong_obj = obj
2455 self.dispatch.after_attach(self, state)
2457 if state.key:
2458 self.dispatch.detached_to_persistent(self, state)
2459 else:
2460 self.dispatch.transient_to_pending(self, state)
2462 def __contains__(self, instance):
2463 """Return True if the instance is associated with this session.
2465 The instance may be pending or persistent within the Session for a
2466 result of True.
2468 """
2469 try:
2470 state = attributes.instance_state(instance)
2471 except exc.NO_STATE as err:
2472 util.raise_(
2473 exc.UnmappedInstanceError(instance), replace_context=err,
2474 )
2475 return self._contains_state(state)
2477 def __iter__(self):
2478 """Iterate over all pending or persistent instances within this
2479 Session.
2481 """
2482 return iter(
2483 list(self._new.values()) + list(self.identity_map.values())
2484 )
2486 def _contains_state(self, state):
2487 return state in self._new or self.identity_map.contains_state(state)
2489 def flush(self, objects=None):
2490 """Flush all the object changes to the database.
2492 Writes out all pending object creations, deletions and modifications
2493 to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are
2494 automatically ordered by the Session's unit of work dependency
2495 solver.
2497 Database operations will be issued in the current transactional
2498 context and do not affect the state of the transaction, unless an
2499 error occurs, in which case the entire transaction is rolled back.
2500 You may flush() as often as you like within a transaction to move
2501 changes from Python to the database's transaction buffer.
2503 For ``autocommit`` Sessions with no active manual transaction, flush()
2504 will create a transaction on the fly that surrounds the entire set of
2505 operations into the flush.
2507 :param objects: Optional; restricts the flush operation to operate
2508 only on elements that are in the given collection.
2510 This feature is for an extremely narrow set of use cases where
2511 particular objects may need to be operated upon before the
2512 full flush() occurs. It is not intended for general use.
2514 """
2516 if self._flushing:
2517 raise sa_exc.InvalidRequestError("Session is already flushing")
2519 if self._is_clean():
2520 return
2521 try:
2522 self._flushing = True
2523 self._flush(objects)
2524 finally:
2525 self._flushing = False
2527 def _flush_warning(self, method):
2528 util.warn(
2529 "Usage of the '%s' operation is not currently supported "
2530 "within the execution stage of the flush process. "
2531 "Results may not be consistent. Consider using alternative "
2532 "event listeners or connection-level operations instead." % method
2533 )
2535 def _is_clean(self):
2536 return (
2537 not self.identity_map.check_modified()
2538 and not self._deleted
2539 and not self._new
2540 )
2542 def _flush(self, objects=None):
2544 dirty = self._dirty_states
2545 if not dirty and not self._deleted and not self._new:
2546 self.identity_map._modified.clear()
2547 return
2549 flush_context = UOWTransaction(self)
2551 if self.dispatch.before_flush:
2552 self.dispatch.before_flush(self, flush_context, objects)
2553 # re-establish "dirty states" in case the listeners
2554 # added
2555 dirty = self._dirty_states
2557 deleted = set(self._deleted)
2558 new = set(self._new)
2560 dirty = set(dirty).difference(deleted)
2562 # create the set of all objects we want to operate upon
2563 if objects:
2564 # specific list passed in
2565 objset = set()
2566 for o in objects:
2567 try:
2568 state = attributes.instance_state(o)
2570 except exc.NO_STATE as err:
2571 util.raise_(
2572 exc.UnmappedInstanceError(o), replace_context=err,
2573 )
2574 objset.add(state)
2575 else:
2576 objset = None
2578 # store objects whose fate has been decided
2579 processed = set()
2581 # put all saves/updates into the flush context. detect top-level
2582 # orphans and throw them into deleted.
2583 if objset:
2584 proc = new.union(dirty).intersection(objset).difference(deleted)
2585 else:
2586 proc = new.union(dirty).difference(deleted)
2588 for state in proc:
2589 is_orphan = _state_mapper(state)._is_orphan(state)
2591 is_persistent_orphan = is_orphan and state.has_identity
2593 if (
2594 is_orphan
2595 and not is_persistent_orphan
2596 and state._orphaned_outside_of_session
2597 ):
2598 self._expunge_states([state])
2599 else:
2600 _reg = flush_context.register_object(
2601 state, isdelete=is_persistent_orphan
2602 )
2603 assert _reg, "Failed to add object to the flush context!"
2604 processed.add(state)
2606 # put all remaining deletes into the flush context.
2607 if objset:
2608 proc = deleted.intersection(objset).difference(processed)
2609 else:
2610 proc = deleted.difference(processed)
2611 for state in proc:
2612 _reg = flush_context.register_object(state, isdelete=True)
2613 assert _reg, "Failed to add object to the flush context!"
2615 if not flush_context.has_work:
2616 return
2618 flush_context.transaction = transaction = self.begin(
2619 subtransactions=True
2620 )
2621 try:
2622 self._warn_on_events = True
2623 try:
2624 flush_context.execute()
2625 finally:
2626 self._warn_on_events = False
2628 self.dispatch.after_flush(self, flush_context)
2630 flush_context.finalize_flush_changes()
2632 if not objects and self.identity_map._modified:
2633 len_ = len(self.identity_map._modified)
2635 statelib.InstanceState._commit_all_states(
2636 [
2637 (state, state.dict)
2638 for state in self.identity_map._modified
2639 ],
2640 instance_dict=self.identity_map,
2641 )
2642 util.warn(
2643 "Attribute history events accumulated on %d "
2644 "previously clean instances "
2645 "within inner-flush event handlers have been "
2646 "reset, and will not result in database updates. "
2647 "Consider using set_committed_value() within "
2648 "inner-flush event handlers to avoid this warning." % len_
2649 )
2651 # useful assertions:
2652 # if not objects:
2653 # assert not self.identity_map._modified
2654 # else:
2655 # assert self.identity_map._modified == \
2656 # self.identity_map._modified.difference(objects)
2658 self.dispatch.after_flush_postexec(self, flush_context)
2660 transaction.commit()
2662 except:
2663 with util.safe_reraise():
2664 transaction.rollback(_capture_exception=True)
2666 def bulk_save_objects(
2667 self,
2668 objects,
2669 return_defaults=False,
2670 update_changed_only=True,
2671 preserve_order=True,
2672 ):
2673 """Perform a bulk save of the given list of objects.
2675 The bulk save feature allows mapped objects to be used as the
2676 source of simple INSERT and UPDATE operations which can be more easily
2677 grouped together into higher performing "executemany"
2678 operations; the extraction of data from the objects is also performed
2679 using a lower-latency process that ignores whether or not attributes
2680 have actually been modified in the case of UPDATEs, and also ignores
2681 SQL expressions.
2683 The objects as given are not added to the session and no additional
2684 state is established on them, unless the ``return_defaults`` flag
2685 is also set, in which case primary key attributes and server-side
2686 default values will be populated.
2688 .. versionadded:: 1.0.0
2690 .. warning::
2692 The bulk save feature allows for a lower-latency INSERT/UPDATE
2693 of rows at the expense of most other unit-of-work features.
2694 Features such as object management, relationship handling,
2695 and SQL clause support are **silently omitted** in favor of raw
2696 INSERT/UPDATES of records.
2698 **Please read the list of caveats at** :ref:`bulk_operations`
2699 **before using this method, and fully test and confirm the
2700 functionality of all code developed using these systems.**
2702 :param objects: a sequence of mapped object instances. The mapped
2703 objects are persisted as is, and are **not** associated with the
2704 :class:`.Session` afterwards.
2706 For each object, whether the object is sent as an INSERT or an
2707 UPDATE is dependent on the same rules used by the :class:`.Session`
2708 in traditional operation; if the object has the
2709 :attr:`.InstanceState.key`
2710 attribute set, then the object is assumed to be "detached" and
2711 will result in an UPDATE. Otherwise, an INSERT is used.
2713 In the case of an UPDATE, statements are grouped based on which
2714 attributes have changed, and are thus to be the subject of each
2715 SET clause. If ``update_changed_only`` is False, then all
2716 attributes present within each object are applied to the UPDATE
2717 statement, which may help in allowing the statements to be grouped
2718 together into a larger executemany(), and will also reduce the
2719 overhead of checking history on attributes.
2721 :param return_defaults: when True, rows that are missing values which
2722 generate defaults, namely integer primary key defaults and sequences,
2723 will be inserted **one at a time**, so that the primary key value
2724 is available. In particular this will allow joined-inheritance
2725 and other multi-table mappings to insert correctly without the need
2726 to provide primary key values ahead of time; however,
2727 :paramref:`.Session.bulk_save_objects.return_defaults` **greatly
2728 reduces the performance gains** of the method overall.
2730 :param update_changed_only: when True, UPDATE statements are rendered
2731 based on those attributes in each state that have logged changes.
2732 When False, all attributes present are rendered into the SET clause
2733 with the exception of primary key attributes.
2735 :param preserve_order: when True, the order of inserts and updates
2736 matches exactly the order in which the objects are given. When
2737 False, common types of objects are grouped into inserts
2738 and updates, to allow for more batching opportunities.
2740 .. versionadded:: 1.3
2742 .. seealso::
2744 :ref:`bulk_operations`
2746 :meth:`.Session.bulk_insert_mappings`
2748 :meth:`.Session.bulk_update_mappings`
2750 """
2752 def key(state):
2753 return (state.mapper, state.key is not None)
2755 obj_states = (attributes.instance_state(obj) for obj in objects)
2756 if not preserve_order:
2757 obj_states = sorted(obj_states, key=key)
2759 for (mapper, isupdate), states in itertools.groupby(obj_states, key):
2760 self._bulk_save_mappings(
2761 mapper,
2762 states,
2763 isupdate,
2764 True,
2765 return_defaults,
2766 update_changed_only,
2767 False,
2768 )
2770 def bulk_insert_mappings(
2771 self, mapper, mappings, return_defaults=False, render_nulls=False
2772 ):
2773 """Perform a bulk insert of the given list of mapping dictionaries.
2775 The bulk insert feature allows plain Python dictionaries to be used as
2776 the source of simple INSERT operations which can be more easily
2777 grouped together into higher performing "executemany"
2778 operations. Using dictionaries, there is no "history" or session
2779 state management features in use, reducing latency when inserting
2780 large numbers of simple rows.
2782 The values within the dictionaries as given are typically passed
2783 without modification into Core :meth:`_expression.Insert` constructs,
2784 after
2785 organizing the values within them across the tables to which
2786 the given mapper is mapped.
2788 .. versionadded:: 1.0.0
2790 .. warning::
2792 The bulk insert feature allows for a lower-latency INSERT
2793 of rows at the expense of most other unit-of-work features.
2794 Features such as object management, relationship handling,
2795 and SQL clause support are **silently omitted** in favor of raw
2796 INSERT of records.
2798 **Please read the list of caveats at** :ref:`bulk_operations`
2799 **before using this method, and fully test and confirm the
2800 functionality of all code developed using these systems.**
2802 :param mapper: a mapped class, or the actual :class:`_orm.Mapper`
2803 object,
2804 representing the single kind of object represented within the mapping
2805 list.
2807 :param mappings: a sequence of dictionaries, each one containing the
2808 state of the mapped row to be inserted, in terms of the attribute
2809 names on the mapped class. If the mapping refers to multiple tables,
2810 such as a joined-inheritance mapping, each dictionary must contain all
2811 keys to be populated into all tables.
2813 :param return_defaults: when True, rows that are missing values which
2814 generate defaults, namely integer primary key defaults and sequences,
2815 will be inserted **one at a time**, so that the primary key value
2816 is available. In particular this will allow joined-inheritance
2817 and other multi-table mappings to insert correctly without the need
2818 to provide primary
2819 key values ahead of time; however,
2820 :paramref:`.Session.bulk_insert_mappings.return_defaults`
2821 **greatly reduces the performance gains** of the method overall.
2822 If the rows
2823 to be inserted only refer to a single table, then there is no
2824 reason this flag should be set as the returned default information
2825 is not used.
2827 :param render_nulls: When True, a value of ``None`` will result
2828 in a NULL value being included in the INSERT statement, rather
2829 than the column being omitted from the INSERT. This allows all
2830 the rows being INSERTed to have the identical set of columns which
2831 allows the full set of rows to be batched to the DBAPI. Normally,
2832 each column-set that contains a different combination of NULL values
2833 than the previous row must omit a different series of columns from
2834 the rendered INSERT statement, which means it must be emitted as a
2835 separate statement. By passing this flag, the full set of rows
2836 are guaranteed to be batchable into one batch; the cost however is
2837 that server-side defaults which are invoked by an omitted column will
2838 be skipped, so care must be taken to ensure that these are not
2839 necessary.
2841 .. warning::
2843 When this flag is set, **server side default SQL values will
2844 not be invoked** for those columns that are inserted as NULL;
2845 the NULL value will be sent explicitly. Care must be taken
2846 to ensure that no server-side default functions need to be
2847 invoked for the operation as a whole.
2849 .. versionadded:: 1.1
2851 .. seealso::
2853 :ref:`bulk_operations`
2855 :meth:`.Session.bulk_save_objects`
2857 :meth:`.Session.bulk_update_mappings`
2859 """
2860 self._bulk_save_mappings(
2861 mapper,
2862 mappings,
2863 False,
2864 False,
2865 return_defaults,
2866 False,
2867 render_nulls,
2868 )
2870 def bulk_update_mappings(self, mapper, mappings):
2871 """Perform a bulk update of the given list of mapping dictionaries.
2873 The bulk update feature allows plain Python dictionaries to be used as
2874 the source of simple UPDATE operations which can be more easily
2875 grouped together into higher performing "executemany"
2876 operations. Using dictionaries, there is no "history" or session
2877 state management features in use, reducing latency when updating
2878 large numbers of simple rows.
2880 .. versionadded:: 1.0.0
2882 .. warning::
2884 The bulk update feature allows for a lower-latency UPDATE
2885 of rows at the expense of most other unit-of-work features.
2886 Features such as object management, relationship handling,
2887 and SQL clause support are **silently omitted** in favor of raw
2888 UPDATES of records.
2890 **Please read the list of caveats at** :ref:`bulk_operations`
2891 **before using this method, and fully test and confirm the
2892 functionality of all code developed using these systems.**
2894 :param mapper: a mapped class, or the actual :class:`_orm.Mapper`
2895 object,
2896 representing the single kind of object represented within the mapping
2897 list.
2899 :param mappings: a sequence of dictionaries, each one containing the
2900 state of the mapped row to be updated, in terms of the attribute names
2901 on the mapped class. If the mapping refers to multiple tables, such
2902 as a joined-inheritance mapping, each dictionary may contain keys
2903 corresponding to all tables. All those keys which are present and
2904 are not part of the primary key are applied to the SET clause of the
2905 UPDATE statement; the primary key values, which are required, are
2906 applied to the WHERE clause.
2909 .. seealso::
2911 :ref:`bulk_operations`
2913 :meth:`.Session.bulk_insert_mappings`
2915 :meth:`.Session.bulk_save_objects`
2917 """
2918 self._bulk_save_mappings(
2919 mapper, mappings, True, False, False, False, False
2920 )
2922 def _bulk_save_mappings(
2923 self,
2924 mapper,
2925 mappings,
2926 isupdate,
2927 isstates,
2928 return_defaults,
2929 update_changed_only,
2930 render_nulls,
2931 ):
2932 mapper = _class_to_mapper(mapper)
2933 self._flushing = True
2935 transaction = self.begin(subtransactions=True)
2936 try:
2937 if isupdate:
2938 persistence._bulk_update(
2939 mapper,
2940 mappings,
2941 transaction,
2942 isstates,
2943 update_changed_only,
2944 )
2945 else:
2946 persistence._bulk_insert(
2947 mapper,
2948 mappings,
2949 transaction,
2950 isstates,
2951 return_defaults,
2952 render_nulls,
2953 )
2954 transaction.commit()
2956 except:
2957 with util.safe_reraise():
2958 transaction.rollback(_capture_exception=True)
2959 finally:
2960 self._flushing = False
2962 @util.deprecated_params(
2963 passive=(
2964 "0.8",
2965 "The :paramref:`.Session.is_modified.passive` flag is deprecated "
2966 "and will be removed in a future release. The flag is no longer "
2967 "used and is ignored.",
2968 )
2969 )
2970 def is_modified(self, instance, include_collections=True, passive=None):
2971 r"""Return ``True`` if the given instance has locally
2972 modified attributes.
2974 This method retrieves the history for each instrumented
2975 attribute on the instance and performs a comparison of the current
2976 value to its previously committed value, if any.
2978 It is in effect a more expensive and accurate
2979 version of checking for the given instance in the
2980 :attr:`.Session.dirty` collection; a full test for
2981 each attribute's net "dirty" status is performed.
2983 E.g.::
2985 return session.is_modified(someobject)
2987 A few caveats to this method apply:
2989 * Instances present in the :attr:`.Session.dirty` collection may
2990 report ``False`` when tested with this method. This is because
2991 the object may have received change events via attribute mutation,
2992 thus placing it in :attr:`.Session.dirty`, but ultimately the state
2993 is the same as that loaded from the database, resulting in no net
2994 change here.
2995 * Scalar attributes may not have recorded the previously set
2996 value when a new value was applied, if the attribute was not loaded,
2997 or was expired, at the time the new value was received - in these
2998 cases, the attribute is assumed to have a change, even if there is
2999 ultimately no net change against its database value. SQLAlchemy in
3000 most cases does not need the "old" value when a set event occurs, so
3001 it skips the expense of a SQL call if the old value isn't present,
3002 based on the assumption that an UPDATE of the scalar value is
3003 usually needed, and in those few cases where it isn't, is less
3004 expensive on average than issuing a defensive SELECT.
3006 The "old" value is fetched unconditionally upon set only if the
3007 attribute container has the ``active_history`` flag set to ``True``.
3008 This flag is set typically for primary key attributes and scalar
3009 object references that are not a simple many-to-one. To set this
3010 flag for any arbitrary mapped column, use the ``active_history``
3011 argument with :func:`.column_property`.
3013 :param instance: mapped instance to be tested for pending changes.
3014 :param include_collections: Indicates if multivalued collections
3015 should be included in the operation. Setting this to ``False`` is a
3016 way to detect only local-column based properties (i.e. scalar columns
3017 or many-to-one foreign keys) that would result in an UPDATE for this
3018 instance upon flush.
3019 :param passive: not used
3021 """
3022 state = object_state(instance)
3024 if not state.modified:
3025 return False
3027 dict_ = state.dict
3029 for attr in state.manager.attributes:
3030 if (
3031 not include_collections
3032 and hasattr(attr.impl, "get_collection")
3033 ) or not hasattr(attr.impl, "get_history"):
3034 continue
3036 (added, unchanged, deleted) = attr.impl.get_history(
3037 state, dict_, passive=attributes.NO_CHANGE
3038 )
3040 if added or deleted:
3041 return True
3042 else:
3043 return False
3045 @property
3046 def is_active(self):
3047 """True if this :class:`.Session` is in "transaction mode" and
3048 is not in "partial rollback" state.
3050 The :class:`.Session` in its default mode of ``autocommit=False``
3051 is essentially always in "transaction mode", in that a
3052 :class:`.SessionTransaction` is associated with it as soon as
3053 it is instantiated. This :class:`.SessionTransaction` is immediately
3054 replaced with a new one as soon as it is ended, due to a rollback,
3055 commit, or close operation.
3057 "Transaction mode" does *not* indicate whether
3058 or not actual database connection resources are in use; the
3059 :class:`.SessionTransaction` object coordinates among zero or more
3060 actual database transactions, and starts out with none, accumulating
3061 individual DBAPI connections as different data sources are used
3062 within its scope. The best way to track when a particular
3063 :class:`.Session` has actually begun to use DBAPI resources is to
3064 implement a listener using the :meth:`.SessionEvents.after_begin`
3065 method, which will deliver both the :class:`.Session` as well as the
3066 target :class:`_engine.Connection` to a user-defined event listener.
3068 The "partial rollback" state refers to when an "inner" transaction,
3069 typically used during a flush, encounters an error and emits a
3070 rollback of the DBAPI connection. At this point, the
3071 :class:`.Session` is in "partial rollback" and awaits for the user to
3072 call :meth:`.Session.rollback`, in order to close out the
3073 transaction stack. It is in this "partial rollback" period that the
3074 :attr:`.is_active` flag returns False. After the call to
3075 :meth:`.Session.rollback`, the :class:`.SessionTransaction` is
3076 replaced with a new one and :attr:`.is_active` returns ``True`` again.
3078 When a :class:`.Session` is used in ``autocommit=True`` mode, the
3079 :class:`.SessionTransaction` is only instantiated within the scope
3080 of a flush call, or when :meth:`.Session.begin` is called. So
3081 :attr:`.is_active` will always be ``False`` outside of a flush or
3082 :meth:`.Session.begin` block in this mode, and will be ``True``
3083 within the :meth:`.Session.begin` block as long as it doesn't enter
3084 "partial rollback" state.
3086 From all the above, it follows that the only purpose to this flag is
3087 for application frameworks that wish to detect if a "rollback" is
3088 necessary within a generic error handling routine, for
3089 :class:`.Session` objects that would otherwise be in
3090 "partial rollback" mode. In a typical integration case, this is also
3091 not necessary as it is standard practice to emit
3092 :meth:`.Session.rollback` unconditionally within the outermost
3093 exception catch.
3095 To track the transactional state of a :class:`.Session` fully,
3096 use event listeners, primarily the :meth:`.SessionEvents.after_begin`,
3097 :meth:`.SessionEvents.after_commit`,
3098 :meth:`.SessionEvents.after_rollback` and related events.
3100 """
3101 return self.transaction and self.transaction.is_active
3103 identity_map = None
3104 """A mapping of object identities to objects themselves.
3106 Iterating through ``Session.identity_map.values()`` provides
3107 access to the full set of persistent objects (i.e., those
3108 that have row identity) currently in the session.
3110 .. seealso::
3112 :func:`.identity_key` - helper function to produce the keys used
3113 in this dictionary.
3115 """
3117 @property
3118 def _dirty_states(self):
3119 """The set of all persistent states considered dirty.
3121 This method returns all states that were modified including
3122 those that were possibly deleted.
3124 """
3125 return self.identity_map._dirty_states()
3127 @property
3128 def dirty(self):
3129 """The set of all persistent instances considered dirty.
3131 E.g.::
3133 some_mapped_object in session.dirty
3135 Instances are considered dirty when they were modified but not
3136 deleted.
3138 Note that this 'dirty' calculation is 'optimistic'; most
3139 attribute-setting or collection modification operations will
3140 mark an instance as 'dirty' and place it in this set, even if
3141 there is no net change to the attribute's value. At flush
3142 time, the value of each attribute is compared to its
3143 previously saved value, and if there's no net change, no SQL
3144 operation will occur (this is a more expensive operation so
3145 it's only done at flush time).
3147 To check if an instance has actionable net changes to its
3148 attributes, use the :meth:`.Session.is_modified` method.
3150 """
3151 return util.IdentitySet(
3152 [
3153 state.obj()
3154 for state in self._dirty_states
3155 if state not in self._deleted
3156 ]
3157 )
3159 @property
3160 def deleted(self):
3161 "The set of all instances marked as 'deleted' within this ``Session``"
3163 return util.IdentitySet(list(self._deleted.values()))
3165 @property
3166 def new(self):
3167 "The set of all instances marked as 'new' within this ``Session``."
3169 return util.IdentitySet(list(self._new.values()))
3172class sessionmaker(_SessionClassMethods):
3173 """A configurable :class:`.Session` factory.
3175 The :class:`.sessionmaker` factory generates new
3176 :class:`.Session` objects when called, creating them given
3177 the configurational arguments established here.
3179 e.g.::
3181 # global scope
3182 Session = sessionmaker(autoflush=False)
3184 # later, in a local scope, create and use a session:
3185 sess = Session()
3187 Any keyword arguments sent to the constructor itself will override the
3188 "configured" keywords::
3190 Session = sessionmaker()
3192 # bind an individual session to a connection
3193 sess = Session(bind=connection)
3195 The class also includes a method :meth:`.configure`, which can
3196 be used to specify additional keyword arguments to the factory, which
3197 will take effect for subsequent :class:`.Session` objects generated.
3198 This is usually used to associate one or more :class:`_engine.Engine`
3199 objects
3200 with an existing :class:`.sessionmaker` factory before it is first
3201 used::
3203 # application starts
3204 Session = sessionmaker()
3206 # ... later
3207 engine = create_engine('sqlite:///foo.db')
3208 Session.configure(bind=engine)
3210 sess = Session()
3212 .. seealso:
3214 :ref:`session_getting` - introductory text on creating
3215 sessions using :class:`.sessionmaker`.
3217 """
3219 def __init__(
3220 self,
3221 bind=None,
3222 class_=Session,
3223 autoflush=True,
3224 autocommit=False,
3225 expire_on_commit=True,
3226 info=None,
3227 **kw
3228 ):
3229 r"""Construct a new :class:`.sessionmaker`.
3231 All arguments here except for ``class_`` correspond to arguments
3232 accepted by :class:`.Session` directly. See the
3233 :meth:`.Session.__init__` docstring for more details on parameters.
3235 :param bind: a :class:`_engine.Engine` or other :class:`.Connectable`
3236 with
3237 which newly created :class:`.Session` objects will be associated.
3238 :param class\_: class to use in order to create new :class:`.Session`
3239 objects. Defaults to :class:`.Session`.
3240 :param autoflush: The autoflush setting to use with newly created
3241 :class:`.Session` objects.
3242 :param autocommit: The autocommit setting to use with newly created
3243 :class:`.Session` objects.
3244 :param expire_on_commit=True: the expire_on_commit setting to use
3245 with newly created :class:`.Session` objects.
3246 :param info: optional dictionary of information that will be available
3247 via :attr:`.Session.info`. Note this dictionary is *updated*, not
3248 replaced, when the ``info`` parameter is specified to the specific
3249 :class:`.Session` construction operation.
3251 .. versionadded:: 0.9.0
3253 :param \**kw: all other keyword arguments are passed to the
3254 constructor of newly created :class:`.Session` objects.
3256 """
3257 kw["bind"] = bind
3258 kw["autoflush"] = autoflush
3259 kw["autocommit"] = autocommit
3260 kw["expire_on_commit"] = expire_on_commit
3261 if info is not None:
3262 kw["info"] = info
3263 self.kw = kw
3264 # make our own subclass of the given class, so that
3265 # events can be associated with it specifically.
3266 self.class_ = type(class_.__name__, (class_,), {})
3268 def __call__(self, **local_kw):
3269 """Produce a new :class:`.Session` object using the configuration
3270 established in this :class:`.sessionmaker`.
3272 In Python, the ``__call__`` method is invoked on an object when
3273 it is "called" in the same way as a function::
3275 Session = sessionmaker()
3276 session = Session() # invokes sessionmaker.__call__()
3278 """
3279 for k, v in self.kw.items():
3280 if k == "info" and "info" in local_kw:
3281 d = v.copy()
3282 d.update(local_kw["info"])
3283 local_kw["info"] = d
3284 else:
3285 local_kw.setdefault(k, v)
3286 return self.class_(**local_kw)
3288 def configure(self, **new_kw):
3289 """(Re)configure the arguments for this sessionmaker.
3291 e.g.::
3293 Session = sessionmaker()
3295 Session.configure(bind=create_engine('sqlite://'))
3296 """
3297 self.kw.update(new_kw)
3299 def __repr__(self):
3300 return "%s(class_=%r, %s)" % (
3301 self.__class__.__name__,
3302 self.class_.__name__,
3303 ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()),
3304 )
3307def close_all_sessions():
3308 """Close all sessions in memory.
3310 This function consults a global registry of all :class:`.Session` objects
3311 and calls :meth:`.Session.close` on them, which resets them to a clean
3312 state.
3314 This function is not for general use but may be useful for test suites
3315 within the teardown scheme.
3317 .. versionadded:: 1.3
3319 """
3321 for sess in _sessions.values():
3322 sess.close()
3325def make_transient(instance):
3326 """Alter the state of the given instance so that it is :term:`transient`.
3328 .. note::
3330 :func:`.make_transient` is a special-case function for
3331 advanced use cases only.
3333 The given mapped instance is assumed to be in the :term:`persistent` or
3334 :term:`detached` state. The function will remove its association with any
3335 :class:`.Session` as well as its :attr:`.InstanceState.identity`. The
3336 effect is that the object will behave as though it were newly constructed,
3337 except retaining any attribute / collection values that were loaded at the
3338 time of the call. The :attr:`.InstanceState.deleted` flag is also reset
3339 if this object had been deleted as a result of using
3340 :meth:`.Session.delete`.
3342 .. warning::
3344 :func:`.make_transient` does **not** "unexpire" or otherwise eagerly
3345 load ORM-mapped attributes that are not currently loaded at the time
3346 the function is called. This includes attributes which:
3348 * were expired via :meth:`.Session.expire`
3350 * were expired as the natural effect of committing a session
3351 transaction, e.g. :meth:`.Session.commit`
3353 * are normally :term:`lazy loaded` but are not currently loaded
3355 * are "deferred" via :ref:`deferred` and are not yet loaded
3357 * were not present in the query which loaded this object, such as that
3358 which is common in joined table inheritance and other scenarios.
3360 After :func:`.make_transient` is called, unloaded attributes such
3361 as those above will normally resolve to the value ``None`` when
3362 accessed, or an empty collection for a collection-oriented attribute.
3363 As the object is transient and un-associated with any database
3364 identity, it will no longer retrieve these values.
3366 .. seealso::
3368 :func:`.make_transient_to_detached`
3370 """
3371 state = attributes.instance_state(instance)
3372 s = _state_session(state)
3373 if s:
3374 s._expunge_states([state])
3376 # remove expired state
3377 state.expired_attributes.clear()
3379 # remove deferred callables
3380 if state.callables:
3381 del state.callables
3383 if state.key:
3384 del state.key
3385 if state._deleted:
3386 del state._deleted
3389def make_transient_to_detached(instance):
3390 """Make the given transient instance :term:`detached`.
3392 .. note::
3394 :func:`.make_transient_to_detached` is a special-case function for
3395 advanced use cases only.
3397 All attribute history on the given instance
3398 will be reset as though the instance were freshly loaded
3399 from a query. Missing attributes will be marked as expired.
3400 The primary key attributes of the object, which are required, will be made
3401 into the "key" of the instance.
3403 The object can then be added to a session, or merged
3404 possibly with the load=False flag, at which point it will look
3405 as if it were loaded that way, without emitting SQL.
3407 This is a special use case function that differs from a normal
3408 call to :meth:`.Session.merge` in that a given persistent state
3409 can be manufactured without any SQL calls.
3411 .. versionadded:: 0.9.5
3413 .. seealso::
3415 :func:`.make_transient`
3417 :meth:`.Session.enable_relationship_loading`
3419 """
3420 state = attributes.instance_state(instance)
3421 if state.session_id or state.key:
3422 raise sa_exc.InvalidRequestError("Given object must be transient")
3423 state.key = state.mapper._identity_key_from_state(state)
3424 if state._deleted:
3425 del state._deleted
3426 state._commit_all(state.dict)
3427 state._expire_attributes(state.dict, state.unloaded_expirable)
3430def object_session(instance):
3431 """Return the :class:`.Session` to which the given instance belongs.
3433 This is essentially the same as the :attr:`.InstanceState.session`
3434 accessor. See that attribute for details.
3436 """
3438 try:
3439 state = attributes.instance_state(instance)
3440 except exc.NO_STATE as err:
3441 util.raise_(
3442 exc.UnmappedInstanceError(instance), replace_context=err,
3443 )
3444 else:
3445 return _state_session(state)
3448_new_sessionid = util.counter()