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