1# orm/session.py 2# Copyright (C) 2005-2021 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: https://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 context 16from . import exc 17from . import identity 18from . import loading 19from . import persistence 20from . import query 21from . import state as statelib 22from .base import _class_to_mapper 23from .base import _none_set 24from .base import _state_mapper 25from .base import instance_str 26from .base import object_mapper 27from .base import object_state 28from .base import state_str 29from .unitofwork import UOWTransaction 30from .. import engine 31from .. import exc as sa_exc 32from .. import sql 33from .. import util 34from ..engine.util import TransactionalContext 35from ..inspection import inspect 36from ..sql import coercions 37from ..sql import dml 38from ..sql import roles 39from ..sql import visitors 40from ..sql.base import CompileState 41from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 42 43__all__ = [ 44 "Session", 45 "SessionTransaction", 46 "sessionmaker", 47 "ORMExecuteState", 48 "close_all_sessions", 49 "make_transient", 50 "make_transient_to_detached", 51 "object_session", 52] 53 54_sessions = weakref.WeakValueDictionary() 55"""Weak-referencing dictionary of :class:`.Session` objects. 56""" 57 58statelib._sessions = _sessions 59 60 61def _state_session(state): 62 """Given an :class:`.InstanceState`, return the :class:`.Session` 63 associated, if any. 64 """ 65 return state.session 66 67 68class _SessionClassMethods(object): 69 """Class-level methods for :class:`.Session`, :class:`.sessionmaker`.""" 70 71 @classmethod 72 @util.deprecated( 73 "1.3", 74 "The :meth:`.Session.close_all` method is deprecated and will be " 75 "removed in a future release. Please refer to " 76 ":func:`.session.close_all_sessions`.", 77 ) 78 def close_all(cls): 79 """Close *all* sessions in memory.""" 80 81 close_all_sessions() 82 83 @classmethod 84 @util.preload_module("sqlalchemy.orm.util") 85 def identity_key(cls, *args, **kwargs): 86 """Return an identity key. 87 88 This is an alias of :func:`.util.identity_key`. 89 90 """ 91 return util.preloaded.orm_util.identity_key(*args, **kwargs) 92 93 @classmethod 94 def object_session(cls, instance): 95 """Return the :class:`.Session` to which an object belongs. 96 97 This is an alias of :func:`.object_session`. 98 99 """ 100 101 return object_session(instance) 102 103 104ACTIVE = util.symbol("ACTIVE") 105PREPARED = util.symbol("PREPARED") 106COMMITTED = util.symbol("COMMITTED") 107DEACTIVE = util.symbol("DEACTIVE") 108CLOSED = util.symbol("CLOSED") 109 110 111class ORMExecuteState(util.MemoizedSlots): 112 """Represents a call to the :meth:`_orm.Session.execute` method, as passed 113 to the :meth:`.SessionEvents.do_orm_execute` event hook. 114 115 .. versionadded:: 1.4 116 117 .. seealso:: 118 119 :ref:`session_execute_events` - top level documentation on how 120 to use :meth:`_orm.SessionEvents.do_orm_execute` 121 122 """ 123 124 __slots__ = ( 125 "session", 126 "statement", 127 "parameters", 128 "execution_options", 129 "local_execution_options", 130 "bind_arguments", 131 "_compile_state_cls", 132 "_starting_event_idx", 133 "_events_todo", 134 "_update_execution_options", 135 ) 136 137 def __init__( 138 self, 139 session, 140 statement, 141 parameters, 142 execution_options, 143 bind_arguments, 144 compile_state_cls, 145 events_todo, 146 ): 147 self.session = session 148 self.statement = statement 149 self.parameters = parameters 150 self.local_execution_options = execution_options 151 self.execution_options = statement._execution_options.union( 152 execution_options 153 ) 154 self.bind_arguments = bind_arguments 155 self._compile_state_cls = compile_state_cls 156 self._events_todo = list(events_todo) 157 158 def _remaining_events(self): 159 return self._events_todo[self._starting_event_idx + 1 :] 160 161 def invoke_statement( 162 self, 163 statement=None, 164 params=None, 165 execution_options=None, 166 bind_arguments=None, 167 ): 168 """Execute the statement represented by this 169 :class:`.ORMExecuteState`, without re-invoking events that have 170 already proceeded. 171 172 This method essentially performs a re-entrant execution of the current 173 statement for which the :meth:`.SessionEvents.do_orm_execute` event is 174 being currently invoked. The use case for this is for event handlers 175 that want to override how the ultimate 176 :class:`_engine.Result` object is returned, such as for schemes that 177 retrieve results from an offline cache or which concatenate results 178 from multiple executions. 179 180 When the :class:`_engine.Result` object is returned by the actual 181 handler function within :meth:`_orm.SessionEvents.do_orm_execute` and 182 is propagated to the calling 183 :meth:`_orm.Session.execute` method, the remainder of the 184 :meth:`_orm.Session.execute` method is preempted and the 185 :class:`_engine.Result` object is returned to the caller of 186 :meth:`_orm.Session.execute` immediately. 187 188 :param statement: optional statement to be invoked, in place of the 189 statement currently represented by :attr:`.ORMExecuteState.statement`. 190 191 :param params: optional dictionary of parameters which will be merged 192 into the existing :attr:`.ORMExecuteState.parameters` of this 193 :class:`.ORMExecuteState`. 194 195 :param execution_options: optional dictionary of execution options 196 will be merged into the existing 197 :attr:`.ORMExecuteState.execution_options` of this 198 :class:`.ORMExecuteState`. 199 200 :param bind_arguments: optional dictionary of bind_arguments 201 which will be merged amongst the current 202 :attr:`.ORMExecuteState.bind_arguments` 203 of this :class:`.ORMExecuteState`. 204 205 :return: a :class:`_engine.Result` object with ORM-level results. 206 207 .. seealso:: 208 209 :ref:`do_orm_execute_re_executing` - background and examples on the 210 appropriate usage of :meth:`_orm.ORMExecuteState.invoke_statement`. 211 212 213 """ 214 215 if statement is None: 216 statement = self.statement 217 218 _bind_arguments = dict(self.bind_arguments) 219 if bind_arguments: 220 _bind_arguments.update(bind_arguments) 221 _bind_arguments["_sa_skip_events"] = True 222 223 if params: 224 _params = dict(self.parameters) 225 _params.update(params) 226 else: 227 _params = self.parameters 228 229 _execution_options = self.local_execution_options 230 if execution_options: 231 _execution_options = _execution_options.union(execution_options) 232 233 return self.session.execute( 234 statement, 235 _params, 236 _execution_options, 237 _bind_arguments, 238 _parent_execute_state=self, 239 ) 240 241 @property 242 def bind_mapper(self): 243 """Return the :class:`_orm.Mapper` that is the primary "bind" mapper. 244 245 For an :class:`_orm.ORMExecuteState` object invoking an ORM 246 statement, that is, the :attr:`_orm.ORMExecuteState.is_orm_statement` 247 attribute is ``True``, this attribute will return the 248 :class:`_orm.Mapper` that is considered to be the "primary" mapper 249 of the statement. The term "bind mapper" refers to the fact that 250 a :class:`_orm.Session` object may be "bound" to multiple 251 :class:`_engine.Engine` objects keyed to mapped classes, and the 252 "bind mapper" determines which of those :class:`_engine.Engine` objects 253 would be selected. 254 255 For a statement that is invoked against a single mapped class, 256 :attr:`_orm.ORMExecuteState.bind_mapper` is intended to be a reliable 257 way of getting this mapper. 258 259 .. versionadded:: 1.4.0b2 260 261 .. seealso:: 262 263 :attr:`_orm.ORMExecuteState.all_mappers` 264 265 266 """ 267 return self.bind_arguments.get("mapper", None) 268 269 @property 270 def all_mappers(self): 271 """Return a sequence of all :class:`_orm.Mapper` objects that are 272 involved at the top level of this statement. 273 274 By "top level" we mean those :class:`_orm.Mapper` objects that would 275 be represented in the result set rows for a :func:`_sql.select` 276 query, or for a :func:`_dml.update` or :func:`_dml.delete` query, 277 the mapper that is the main subject of the UPDATE or DELETE. 278 279 .. versionadded:: 1.4.0b2 280 281 .. seealso:: 282 283 :attr:`_orm.ORMExecuteState.bind_mapper` 284 285 286 287 """ 288 if not self.is_orm_statement: 289 return [] 290 elif self.is_select: 291 result = [] 292 seen = set() 293 for d in self.statement.column_descriptions: 294 ent = d["entity"] 295 if ent: 296 insp = inspect(ent, raiseerr=False) 297 if insp and insp.mapper and insp.mapper not in seen: 298 seen.add(insp.mapper) 299 result.append(insp.mapper) 300 return result 301 elif self.is_update or self.is_delete: 302 return [self.bind_mapper] 303 else: 304 return [] 305 306 @property 307 def is_orm_statement(self): 308 """return True if the operation is an ORM statement. 309 310 This indicates that the select(), update(), or delete() being 311 invoked contains ORM entities as subjects. For a statement 312 that does not have ORM entities and instead refers only to 313 :class:`.Table` metadata, it is invoked as a Core SQL statement 314 and no ORM-level automation takes place. 315 316 """ 317 return self._compile_state_cls is not None 318 319 @property 320 def is_select(self): 321 """return True if this is a SELECT operation.""" 322 return self.statement.is_select 323 324 @property 325 def is_insert(self): 326 """return True if this is an INSERT operation.""" 327 return self.statement.is_dml and self.statement.is_insert 328 329 @property 330 def is_update(self): 331 """return True if this is an UPDATE operation.""" 332 return self.statement.is_dml and self.statement.is_update 333 334 @property 335 def is_delete(self): 336 """return True if this is a DELETE operation.""" 337 return self.statement.is_dml and self.statement.is_delete 338 339 @property 340 def _is_crud(self): 341 return isinstance(self.statement, (dml.Update, dml.Delete)) 342 343 def update_execution_options(self, **opts): 344 # TODO: no coverage 345 self.local_execution_options = self.local_execution_options.union(opts) 346 347 def _orm_compile_options(self): 348 if not self.is_select: 349 return None 350 opts = self.statement._compile_options 351 if opts.isinstance(context.ORMCompileState.default_compile_options): 352 return opts 353 else: 354 return None 355 356 @property 357 def lazy_loaded_from(self): 358 """An :class:`.InstanceState` that is using this statement execution 359 for a lazy load operation. 360 361 The primary rationale for this attribute is to support the horizontal 362 sharding extension, where it is available within specific query 363 execution time hooks created by this extension. To that end, the 364 attribute is only intended to be meaningful at **query execution 365 time**, and importantly not any time prior to that, including query 366 compilation time. 367 368 """ 369 return self.load_options._lazy_loaded_from 370 371 @property 372 def loader_strategy_path(self): 373 """Return the :class:`.PathRegistry` for the current load path. 374 375 This object represents the "path" in a query along relationships 376 when a particular object or collection is being loaded. 377 378 """ 379 opts = self._orm_compile_options() 380 if opts is not None: 381 return opts._current_path 382 else: 383 return None 384 385 @property 386 def is_column_load(self): 387 """Return True if the operation is refreshing column-oriented 388 attributes on an existing ORM object. 389 390 This occurs during operations such as :meth:`_orm.Session.refresh`, 391 as well as when an attribute deferred by :func:`_orm.defer` is 392 being loaded, or an attribute that was expired either directly 393 by :meth:`_orm.Session.expire` or via a commit operation is being 394 loaded. 395 396 Handlers will very likely not want to add any options to queries 397 when such an operation is occurring as the query should be a straight 398 primary key fetch which should not have any additional WHERE criteria, 399 and loader options travelling with the instance 400 will have already been added to the query. 401 402 .. versionadded:: 1.4.0b2 403 404 .. seealso:: 405 406 :attr:`_orm.ORMExecuteState.is_relationship_load` 407 408 """ 409 opts = self._orm_compile_options() 410 return opts is not None and opts._for_refresh_state 411 412 @property 413 def is_relationship_load(self): 414 """Return True if this load is loading objects on behalf of a 415 relationship. 416 417 This means, the loader in effect is either a LazyLoader, 418 SelectInLoader, SubqueryLoader, or similar, and the entire 419 SELECT statement being emitted is on behalf of a relationship 420 load. 421 422 Handlers will very likely not want to add any options to queries 423 when such an operation is occurring, as loader options are already 424 capable of being propagated to relationship loaders and should 425 be already present. 426 427 .. seealso:: 428 429 :attr:`_orm.ORMExecuteState.is_column_load` 430 431 """ 432 opts = self._orm_compile_options() 433 if opts is None: 434 return False 435 path = self.loader_strategy_path 436 return path is not None and not path.is_root 437 438 @property 439 def load_options(self): 440 """Return the load_options that will be used for this execution.""" 441 442 if not self.is_select: 443 raise sa_exc.InvalidRequestError( 444 "This ORM execution is not against a SELECT statement " 445 "so there are no load options." 446 ) 447 return self.execution_options.get( 448 "_sa_orm_load_options", context.QueryContext.default_load_options 449 ) 450 451 @property 452 def update_delete_options(self): 453 """Return the update_delete_options that will be used for this 454 execution.""" 455 456 if not self._is_crud: 457 raise sa_exc.InvalidRequestError( 458 "This ORM execution is not against an UPDATE or DELETE " 459 "statement so there are no update options." 460 ) 461 return self.execution_options.get( 462 "_sa_orm_update_options", 463 persistence.BulkUDCompileState.default_update_options, 464 ) 465 466 @property 467 def user_defined_options(self): 468 """The sequence of :class:`.UserDefinedOptions` that have been 469 associated with the statement being invoked. 470 471 """ 472 return [ 473 opt 474 for opt in self.statement._with_options 475 if not opt._is_compile_state and not opt._is_legacy_option 476 ] 477 478 479class SessionTransaction(TransactionalContext): 480 """A :class:`.Session`-level transaction. 481 482 :class:`.SessionTransaction` is produced from the 483 :meth:`_orm.Session.begin` 484 and :meth:`_orm.Session.begin_nested` methods. It's largely an internal 485 object that in modern use provides a context manager for session 486 transactions. 487 488 Documentation on interacting with :class:`_orm.SessionTransaction` is 489 at: :ref:`unitofwork_transaction`. 490 491 492 .. versionchanged:: 1.4 The scoping and API methods to work with the 493 :class:`_orm.SessionTransaction` object directly have been simplified. 494 495 .. seealso:: 496 497 :ref:`unitofwork_transaction` 498 499 :meth:`.Session.begin` 500 501 :meth:`.Session.begin_nested` 502 503 :meth:`.Session.rollback` 504 505 :meth:`.Session.commit` 506 507 :meth:`.Session.in_transaction` 508 509 :meth:`.Session.in_nested_transaction` 510 511 :meth:`.Session.get_transaction` 512 513 :meth:`.Session.get_nested_transaction` 514 515 516 """ 517 518 _rollback_exception = None 519 520 def __init__( 521 self, 522 session, 523 parent=None, 524 nested=False, 525 autobegin=False, 526 ): 527 TransactionalContext._trans_ctx_check(session) 528 529 self.session = session 530 self._connections = {} 531 self._parent = parent 532 self.nested = nested 533 if nested: 534 self._previous_nested_transaction = session._nested_transaction 535 self._state = ACTIVE 536 if not parent and nested: 537 raise sa_exc.InvalidRequestError( 538 "Can't start a SAVEPOINT transaction when no existing " 539 "transaction is in progress" 540 ) 541 542 self._take_snapshot(autobegin=autobegin) 543 544 # make sure transaction is assigned before we call the 545 # dispatch 546 self.session._transaction = self 547 548 self.session.dispatch.after_transaction_create(self.session, self) 549 550 @property 551 def parent(self): 552 """The parent :class:`.SessionTransaction` of this 553 :class:`.SessionTransaction`. 554 555 If this attribute is ``None``, indicates this 556 :class:`.SessionTransaction` is at the top of the stack, and 557 corresponds to a real "COMMIT"/"ROLLBACK" 558 block. If non-``None``, then this is either a "subtransaction" 559 or a "nested" / SAVEPOINT transaction. If the 560 :attr:`.SessionTransaction.nested` attribute is ``True``, then 561 this is a SAVEPOINT, and if ``False``, indicates this a subtransaction. 562 563 .. versionadded:: 1.0.16 - use ._parent for previous versions 564 565 """ 566 return self._parent 567 568 nested = False 569 """Indicates if this is a nested, or SAVEPOINT, transaction. 570 571 When :attr:`.SessionTransaction.nested` is True, it is expected 572 that :attr:`.SessionTransaction.parent` will be True as well. 573 574 """ 575 576 @property 577 def is_active(self): 578 return self.session is not None and self._state is ACTIVE 579 580 def _assert_active( 581 self, 582 prepared_ok=False, 583 rollback_ok=False, 584 deactive_ok=False, 585 closed_msg="This transaction is closed", 586 ): 587 if self._state is COMMITTED: 588 raise sa_exc.InvalidRequestError( 589 "This session is in 'committed' state; no further " 590 "SQL can be emitted within this transaction." 591 ) 592 elif self._state is PREPARED: 593 if not prepared_ok: 594 raise sa_exc.InvalidRequestError( 595 "This session is in 'prepared' state; no further " 596 "SQL can be emitted within this transaction." 597 ) 598 elif self._state is DEACTIVE: 599 if not deactive_ok and not rollback_ok: 600 if self._rollback_exception: 601 raise sa_exc.PendingRollbackError( 602 "This Session's transaction has been rolled back " 603 "due to a previous exception during flush." 604 " To begin a new transaction with this Session, " 605 "first issue Session.rollback()." 606 " Original exception was: %s" 607 % self._rollback_exception, 608 code="7s2a", 609 ) 610 elif not deactive_ok: 611 raise sa_exc.InvalidRequestError( 612 "This session is in 'inactive' state, due to the " 613 "SQL transaction being rolled back; no further " 614 "SQL can be emitted within this transaction." 615 ) 616 elif self._state is CLOSED: 617 raise sa_exc.ResourceClosedError(closed_msg) 618 619 @property 620 def _is_transaction_boundary(self): 621 return self.nested or not self._parent 622 623 def connection(self, bindkey, execution_options=None, **kwargs): 624 self._assert_active() 625 bind = self.session.get_bind(bindkey, **kwargs) 626 return self._connection_for_bind(bind, execution_options) 627 628 def _begin(self, nested=False): 629 self._assert_active() 630 return SessionTransaction(self.session, self, nested=nested) 631 632 def _iterate_self_and_parents(self, upto=None): 633 634 current = self 635 result = () 636 while current: 637 result += (current,) 638 if current._parent is upto: 639 break 640 elif current._parent is None: 641 raise sa_exc.InvalidRequestError( 642 "Transaction %s is not on the active transaction list" 643 % (upto) 644 ) 645 else: 646 current = current._parent 647 648 return result 649 650 def _take_snapshot(self, autobegin=False): 651 if not self._is_transaction_boundary: 652 self._new = self._parent._new 653 self._deleted = self._parent._deleted 654 self._dirty = self._parent._dirty 655 self._key_switches = self._parent._key_switches 656 return 657 658 if not autobegin and not self.session._flushing: 659 self.session.flush() 660 661 self._new = weakref.WeakKeyDictionary() 662 self._deleted = weakref.WeakKeyDictionary() 663 self._dirty = weakref.WeakKeyDictionary() 664 self._key_switches = weakref.WeakKeyDictionary() 665 666 def _restore_snapshot(self, dirty_only=False): 667 """Restore the restoration state taken before a transaction began. 668 669 Corresponds to a rollback. 670 671 """ 672 assert self._is_transaction_boundary 673 674 to_expunge = set(self._new).union(self.session._new) 675 self.session._expunge_states(to_expunge, to_transient=True) 676 677 for s, (oldkey, newkey) in self._key_switches.items(): 678 # we probably can do this conditionally based on 679 # if we expunged or not, but safe_discard does that anyway 680 self.session.identity_map.safe_discard(s) 681 682 # restore the old key 683 s.key = oldkey 684 685 # now restore the object, but only if we didn't expunge 686 if s not in to_expunge: 687 self.session.identity_map.replace(s) 688 689 for s in set(self._deleted).union(self.session._deleted): 690 self.session._update_impl(s, revert_deletion=True) 691 692 assert not self.session._deleted 693 694 for s in self.session.identity_map.all_states(): 695 if not dirty_only or s.modified or s in self._dirty: 696 s._expire(s.dict, self.session.identity_map._modified) 697 698 def _remove_snapshot(self): 699 """Remove the restoration state taken before a transaction began. 700 701 Corresponds to a commit. 702 703 """ 704 assert self._is_transaction_boundary 705 706 if not self.nested and self.session.expire_on_commit: 707 for s in self.session.identity_map.all_states(): 708 s._expire(s.dict, self.session.identity_map._modified) 709 710 statelib.InstanceState._detach_states( 711 list(self._deleted), self.session 712 ) 713 self._deleted.clear() 714 elif self.nested: 715 self._parent._new.update(self._new) 716 self._parent._dirty.update(self._dirty) 717 self._parent._deleted.update(self._deleted) 718 self._parent._key_switches.update(self._key_switches) 719 720 def _connection_for_bind(self, bind, execution_options): 721 self._assert_active() 722 723 if bind in self._connections: 724 if execution_options: 725 util.warn( 726 "Connection is already established for the " 727 "given bind; execution_options ignored" 728 ) 729 return self._connections[bind][0] 730 731 local_connect = False 732 should_commit = True 733 734 if self._parent: 735 conn = self._parent._connection_for_bind(bind, execution_options) 736 if not self.nested: 737 return conn 738 else: 739 if isinstance(bind, engine.Connection): 740 conn = bind 741 if conn.engine in self._connections: 742 raise sa_exc.InvalidRequestError( 743 "Session already has a Connection associated for the " 744 "given Connection's Engine" 745 ) 746 else: 747 conn = bind.connect() 748 local_connect = True 749 750 try: 751 if execution_options: 752 conn = conn.execution_options(**execution_options) 753 754 if self.session.twophase and self._parent is None: 755 transaction = conn.begin_twophase() 756 elif self.nested: 757 transaction = conn.begin_nested() 758 elif conn.in_transaction(): 759 # if given a future connection already in a transaction, don't 760 # commit that transaction unless it is a savepoint 761 if conn.in_nested_transaction(): 762 transaction = conn.get_nested_transaction() 763 else: 764 transaction = conn.get_transaction() 765 should_commit = False 766 else: 767 transaction = conn.begin() 768 except: 769 # connection will not not be associated with this Session; 770 # close it immediately so that it isn't closed under GC 771 if local_connect: 772 conn.close() 773 raise 774 else: 775 bind_is_connection = isinstance(bind, engine.Connection) 776 777 self._connections[conn] = self._connections[conn.engine] = ( 778 conn, 779 transaction, 780 should_commit, 781 not bind_is_connection, 782 ) 783 self.session.dispatch.after_begin(self.session, self, conn) 784 return conn 785 786 def prepare(self): 787 if self._parent is not None or not self.session.twophase: 788 raise sa_exc.InvalidRequestError( 789 "'twophase' mode not enabled, or not root transaction; " 790 "can't prepare." 791 ) 792 self._prepare_impl() 793 794 def _prepare_impl(self): 795 self._assert_active() 796 if self._parent is None or self.nested: 797 self.session.dispatch.before_commit(self.session) 798 799 stx = self.session._transaction 800 if stx is not self: 801 for subtransaction in stx._iterate_self_and_parents(upto=self): 802 subtransaction.commit() 803 804 if not self.session._flushing: 805 for _flush_guard in range(100): 806 if self.session._is_clean(): 807 break 808 self.session.flush() 809 else: 810 raise exc.FlushError( 811 "Over 100 subsequent flushes have occurred within " 812 "session.commit() - is an after_flush() hook " 813 "creating new objects?" 814 ) 815 816 if self._parent is None and self.session.twophase: 817 try: 818 for t in set(self._connections.values()): 819 t[1].prepare() 820 except: 821 with util.safe_reraise(): 822 self.rollback() 823 824 self._state = PREPARED 825 826 def commit(self, _to_root=False): 827 self._assert_active(prepared_ok=True) 828 if self._state is not PREPARED: 829 self._prepare_impl() 830 831 if self._parent is None or self.nested: 832 for conn, trans, should_commit, autoclose in set( 833 self._connections.values() 834 ): 835 if should_commit: 836 trans.commit() 837 838 self._state = COMMITTED 839 self.session.dispatch.after_commit(self.session) 840 841 self._remove_snapshot() 842 843 self.close() 844 845 if _to_root and self._parent: 846 return self._parent.commit(_to_root=True) 847 848 return self._parent 849 850 def rollback(self, _capture_exception=False, _to_root=False): 851 self._assert_active(prepared_ok=True, rollback_ok=True) 852 853 stx = self.session._transaction 854 if stx is not self: 855 for subtransaction in stx._iterate_self_and_parents(upto=self): 856 subtransaction.close() 857 858 boundary = self 859 rollback_err = None 860 if self._state in (ACTIVE, PREPARED): 861 for transaction in self._iterate_self_and_parents(): 862 if transaction._parent is None or transaction.nested: 863 try: 864 for t in set(transaction._connections.values()): 865 t[1].rollback() 866 867 transaction._state = DEACTIVE 868 self.session.dispatch.after_rollback(self.session) 869 except: 870 rollback_err = sys.exc_info() 871 finally: 872 transaction._state = DEACTIVE 873 transaction._restore_snapshot( 874 dirty_only=transaction.nested 875 ) 876 boundary = transaction 877 break 878 else: 879 transaction._state = DEACTIVE 880 881 sess = self.session 882 883 if not rollback_err and not sess._is_clean(): 884 885 # if items were added, deleted, or mutated 886 # here, we need to re-restore the snapshot 887 util.warn( 888 "Session's state has been changed on " 889 "a non-active transaction - this state " 890 "will be discarded." 891 ) 892 boundary._restore_snapshot(dirty_only=boundary.nested) 893 894 self.close() 895 896 if self._parent and _capture_exception: 897 self._parent._rollback_exception = sys.exc_info()[1] 898 899 if rollback_err: 900 util.raise_(rollback_err[1], with_traceback=rollback_err[2]) 901 902 sess.dispatch.after_soft_rollback(sess, self) 903 904 if _to_root and self._parent: 905 return self._parent.rollback(_to_root=True) 906 return self._parent 907 908 def close(self, invalidate=False): 909 if self.nested: 910 self.session._nested_transaction = ( 911 self._previous_nested_transaction 912 ) 913 914 self.session._transaction = self._parent 915 916 if self._parent is None: 917 for connection, transaction, should_commit, autoclose in set( 918 self._connections.values() 919 ): 920 if invalidate: 921 connection.invalidate() 922 if should_commit and transaction.is_active: 923 transaction.close() 924 if autoclose: 925 connection.close() 926 927 self._state = CLOSED 928 self.session.dispatch.after_transaction_end(self.session, self) 929 930 self.session = None 931 self._connections = None 932 933 def _get_subject(self): 934 return self.session 935 936 def _transaction_is_active(self): 937 return self._state is ACTIVE 938 939 def _transaction_is_closed(self): 940 return self._state is CLOSED 941 942 943class Session(_SessionClassMethods): 944 """Manages persistence operations for ORM-mapped objects. 945 946 The Session's usage paradigm is described at :doc:`/orm/session`. 947 948 949 """ 950 951 _is_asyncio = False 952 953 @util.deprecated_params( 954 autocommit=( 955 "2.0", 956 "The :paramref:`.Session.autocommit` parameter is deprecated " 957 "and will be removed in SQLAlchemy version 2.0. The " 958 ':class:`_orm.Session` now features "autobegin" behavior ' 959 "such that the :meth:`.Session.begin` method may be called " 960 "if a transaction has not yet been started yet. See the section " 961 ":ref:`session_explicit_begin` for background.", 962 ), 963 ) 964 def __init__( 965 self, 966 bind=None, 967 autoflush=True, 968 future=False, 969 expire_on_commit=True, 970 autocommit=False, 971 twophase=False, 972 binds=None, 973 enable_baked_queries=True, 974 info=None, 975 query_cls=None, 976 ): 977 r"""Construct a new Session. 978 979 See also the :class:`.sessionmaker` function which is used to 980 generate a :class:`.Session`-producing callable with a given 981 set of arguments. 982 983 :param autocommit: 984 Defaults to ``False``. When ``True``, the 985 :class:`.Session` does not automatically begin transactions for 986 individual statement executions, will acquire connections from the 987 engine on an as-needed basis, releasing to the connection pool 988 after each statement. Flushes will begin and commit (or possibly 989 rollback) their own transaction if no transaction is present. 990 When using this mode, the 991 :meth:`.Session.begin` method may be used to explicitly start 992 transactions, but the usual "autobegin" behavior is not present. 993 994 :param autoflush: When ``True``, all query operations will issue a 995 :meth:`~.Session.flush` call to this ``Session`` before proceeding. 996 This is a convenience feature so that :meth:`~.Session.flush` need 997 not be called repeatedly in order for database queries to retrieve 998 results. It's typical that ``autoflush`` is used in conjunction 999 with ``autocommit=False``. In this scenario, explicit calls to 1000 :meth:`~.Session.flush` are rarely needed; you usually only need to 1001 call :meth:`~.Session.commit` (which flushes) to finalize changes. 1002 1003 :param bind: An optional :class:`_engine.Engine` or 1004 :class:`_engine.Connection` to 1005 which this ``Session`` should be bound. When specified, all SQL 1006 operations performed by this session will execute via this 1007 connectable. 1008 1009 :param binds: A dictionary which may specify any number of 1010 :class:`_engine.Engine` or :class:`_engine.Connection` 1011 objects as the source of 1012 connectivity for SQL operations on a per-entity basis. The keys 1013 of the dictionary consist of any series of mapped classes, 1014 arbitrary Python classes that are bases for mapped classes, 1015 :class:`_schema.Table` objects and :class:`_orm.Mapper` objects. 1016 The 1017 values of the dictionary are then instances of 1018 :class:`_engine.Engine` 1019 or less commonly :class:`_engine.Connection` objects. 1020 Operations which 1021 proceed relative to a particular mapped class will consult this 1022 dictionary for the closest matching entity in order to determine 1023 which :class:`_engine.Engine` should be used for a particular SQL 1024 operation. The complete heuristics for resolution are 1025 described at :meth:`.Session.get_bind`. Usage looks like:: 1026 1027 Session = sessionmaker(binds={ 1028 SomeMappedClass: create_engine('postgresql://engine1'), 1029 SomeDeclarativeBase: create_engine('postgresql://engine2'), 1030 some_mapper: create_engine('postgresql://engine3'), 1031 some_table: create_engine('postgresql://engine4'), 1032 }) 1033 1034 .. seealso:: 1035 1036 :ref:`session_partitioning` 1037 1038 :meth:`.Session.bind_mapper` 1039 1040 :meth:`.Session.bind_table` 1041 1042 :meth:`.Session.get_bind` 1043 1044 1045 :param \class_: Specify an alternate class other than 1046 ``sqlalchemy.orm.session.Session`` which should be used by the 1047 returned class. This is the only argument that is local to the 1048 :class:`.sessionmaker` function, and is not sent directly to the 1049 constructor for ``Session``. 1050 1051 :param enable_baked_queries: defaults to ``True``. A flag consumed 1052 by the :mod:`sqlalchemy.ext.baked` extension to determine if 1053 "baked queries" should be cached, as is the normal operation 1054 of this extension. When set to ``False``, caching as used by 1055 this particular extension is disabled. 1056 1057 .. versionchanged:: 1.4 The ``sqlalchemy.ext.baked`` extension is 1058 legacy and is not used by any of SQLAlchemy's internals. This 1059 flag therefore only affects applications that are making explicit 1060 use of this extension within their own code. 1061 1062 :param expire_on_commit: Defaults to ``True``. When ``True``, all 1063 instances will be fully expired after each :meth:`~.commit`, 1064 so that all attribute/object access subsequent to a completed 1065 transaction will load from the most recent database state. 1066 1067 .. seealso:: 1068 1069 :ref:`session_committing` 1070 1071 :param future: if True, use 2.0 style transactional and engine 1072 behavior. Future mode includes the following behaviors: 1073 1074 * The :class:`_orm.Session` will not use "bound" metadata in order 1075 to locate an :class:`_engine.Engine`; the engine or engines in use 1076 must be specified to the constructor of :class:`_orm.Session` or 1077 otherwise be configured against the :class:`_orm.sessionmaker` 1078 in use 1079 1080 * The "subtransactions" feature of :meth:`_orm.Session.begin` is 1081 removed in version 2.0 and is disabled when the future flag is 1082 set. 1083 1084 * The behavior of the :paramref:`_orm.relationship.cascade_backrefs` 1085 flag on a :func:`_orm.relationship` will always assume 1086 "False" behavior. 1087 1088 .. versionadded:: 1.4 1089 1090 .. seealso:: 1091 1092 :ref:`migration_20_toplevel` 1093 1094 :param info: optional dictionary of arbitrary data to be associated 1095 with this :class:`.Session`. Is available via the 1096 :attr:`.Session.info` attribute. Note the dictionary is copied at 1097 construction time so that modifications to the per- 1098 :class:`.Session` dictionary will be local to that 1099 :class:`.Session`. 1100 1101 :param query_cls: Class which should be used to create new Query 1102 objects, as returned by the :meth:`~.Session.query` method. 1103 Defaults to :class:`_query.Query`. 1104 1105 :param twophase: When ``True``, all transactions will be started as 1106 a "two phase" transaction, i.e. using the "two phase" semantics 1107 of the database in use along with an XID. During a 1108 :meth:`~.commit`, after :meth:`~.flush` has been issued for all 1109 attached databases, the :meth:`~.TwoPhaseTransaction.prepare` 1110 method on each database's :class:`.TwoPhaseTransaction` will be 1111 called. This allows each database to roll back the entire 1112 transaction, before each transaction is committed. 1113 1114 """ 1115 self.identity_map = identity.WeakInstanceDict() 1116 1117 self._new = {} # InstanceState->object, strong refs object 1118 self._deleted = {} # same 1119 self.bind = bind 1120 self.__binds = {} 1121 self._flushing = False 1122 self._warn_on_events = False 1123 self._transaction = None 1124 self._nested_transaction = None 1125 self.future = future 1126 self.hash_key = _new_sessionid() 1127 self.autoflush = autoflush 1128 self.expire_on_commit = expire_on_commit 1129 self.enable_baked_queries = enable_baked_queries 1130 1131 if autocommit: 1132 if future: 1133 raise sa_exc.ArgumentError( 1134 "Cannot use autocommit mode with future=True." 1135 ) 1136 self.autocommit = True 1137 else: 1138 self.autocommit = False 1139 1140 self.twophase = twophase 1141 self._query_cls = query_cls if query_cls else query.Query 1142 if info: 1143 self.info.update(info) 1144 1145 if binds is not None: 1146 for key, bind in binds.items(): 1147 self._add_bind(key, bind) 1148 1149 _sessions[self.hash_key] = self 1150 1151 # used by sqlalchemy.engine.util.TransactionalContext 1152 _trans_context_manager = None 1153 1154 connection_callable = None 1155 1156 def __enter__(self): 1157 return self 1158 1159 def __exit__(self, type_, value, traceback): 1160 self.close() 1161 1162 @util.contextmanager 1163 def _maker_context_manager(self): 1164 with self: 1165 with self.begin(): 1166 yield self 1167 1168 @property 1169 @util.deprecated_20( 1170 ":attr:`_orm.Session.transaction`", 1171 alternative="For context manager use, use " 1172 ":meth:`_orm.Session.begin`. To access " 1173 "the current root transaction, use " 1174 ":meth:`_orm.Session.get_transaction`.", 1175 warn_on_attribute_access=True, 1176 ) 1177 def transaction(self): 1178 """The current active or inactive :class:`.SessionTransaction`. 1179 1180 May be None if no transaction has begun yet. 1181 1182 .. versionchanged:: 1.4 the :attr:`.Session.transaction` attribute 1183 is now a read-only descriptor that also may return None if no 1184 transaction has begun yet. 1185 1186 1187 """ 1188 return self._legacy_transaction() 1189 1190 def _legacy_transaction(self): 1191 if not self.future: 1192 self._autobegin() 1193 return self._transaction 1194 1195 def in_transaction(self): 1196 """Return True if this :class:`_orm.Session` has begun a transaction. 1197 1198 .. versionadded:: 1.4 1199 1200 .. seealso:: 1201 1202 :attr:`_orm.Session.is_active` 1203 1204 1205 """ 1206 return self._transaction is not None 1207 1208 def in_nested_transaction(self): 1209 """Return True if this :class:`_orm.Session` has begun a nested 1210 transaction, e.g. SAVEPOINT. 1211 1212 .. versionadded:: 1.4 1213 1214 """ 1215 return self._nested_transaction is not None 1216 1217 def get_transaction(self): 1218 """Return the current root transaction in progress, if any. 1219 1220 .. versionadded:: 1.4 1221 1222 """ 1223 trans = self._transaction 1224 while trans is not None and trans._parent is not None: 1225 trans = trans._parent 1226 return trans 1227 1228 def get_nested_transaction(self): 1229 """Return the current nested transaction in progress, if any. 1230 1231 .. versionadded:: 1.4 1232 1233 """ 1234 1235 return self._nested_transaction 1236 1237 @util.memoized_property 1238 def info(self): 1239 """A user-modifiable dictionary. 1240 1241 The initial value of this dictionary can be populated using the 1242 ``info`` argument to the :class:`.Session` constructor or 1243 :class:`.sessionmaker` constructor or factory methods. The dictionary 1244 here is always local to this :class:`.Session` and can be modified 1245 independently of all other :class:`.Session` objects. 1246 1247 """ 1248 return {} 1249 1250 def _autobegin(self): 1251 if not self.autocommit and self._transaction is None: 1252 1253 trans = SessionTransaction(self, autobegin=True) 1254 assert self._transaction is trans 1255 return True 1256 1257 return False 1258 1259 @util.deprecated_params( 1260 subtransactions=( 1261 "2.0", 1262 "The :paramref:`_orm.Session.begin.subtransactions` flag is " 1263 "deprecated and " 1264 "will be removed in SQLAlchemy version 2.0. See " 1265 "the documentation at :ref:`session_subtransactions` for " 1266 "background on a compatible alternative pattern.", 1267 ) 1268 ) 1269 def begin(self, subtransactions=False, nested=False, _subtrans=False): 1270 """Begin a transaction, or nested transaction, 1271 on this :class:`.Session`, if one is not already begun. 1272 1273 The :class:`_orm.Session` object features **autobegin** behavior, 1274 so that normally it is not necessary to call the 1275 :meth:`_orm.Session.begin` 1276 method explicitly. However, it may be used in order to control 1277 the scope of when the transactional state is begun. 1278 1279 When used to begin the outermost transaction, an error is raised 1280 if this :class:`.Session` is already inside of a transaction. 1281 1282 :param nested: if True, begins a SAVEPOINT transaction and is 1283 equivalent to calling :meth:`~.Session.begin_nested`. For 1284 documentation on SAVEPOINT transactions, please see 1285 :ref:`session_begin_nested`. 1286 1287 :param subtransactions: if True, indicates that this 1288 :meth:`~.Session.begin` can create a "subtransaction". 1289 1290 :return: the :class:`.SessionTransaction` object. Note that 1291 :class:`.SessionTransaction` 1292 acts as a Python context manager, allowing :meth:`.Session.begin` 1293 to be used in a "with" block. See :ref:`session_autocommit` for 1294 an example. 1295 1296 .. seealso:: 1297 1298 :ref:`session_autobegin` 1299 1300 :ref:`unitofwork_transaction` 1301 1302 :meth:`.Session.begin_nested` 1303 1304 1305 """ 1306 1307 if subtransactions and self.future: 1308 raise NotImplementedError( 1309 "subtransactions are not implemented in future " 1310 "Session objects." 1311 ) 1312 1313 if self._autobegin(): 1314 if not subtransactions and not nested and not _subtrans: 1315 return self._transaction 1316 1317 if self._transaction is not None: 1318 if subtransactions or _subtrans or nested: 1319 trans = self._transaction._begin(nested=nested) 1320 assert self._transaction is trans 1321 if nested: 1322 self._nested_transaction = trans 1323 else: 1324 raise sa_exc.InvalidRequestError( 1325 "A transaction is already begun on this Session." 1326 ) 1327 elif not self.autocommit: 1328 # outermost transaction. must be a not nested and not 1329 # a subtransaction 1330 1331 assert not nested and not _subtrans and not subtransactions 1332 trans = SessionTransaction(self) 1333 assert self._transaction is trans 1334 else: 1335 # legacy autocommit mode 1336 assert not self.future 1337 trans = SessionTransaction(self, nested=nested) 1338 assert self._transaction is trans 1339 1340 return self._transaction # needed for __enter__/__exit__ hook 1341 1342 def begin_nested(self): 1343 """Begin a "nested" transaction on this Session, e.g. SAVEPOINT. 1344 1345 The target database(s) and associated drivers must support SQL 1346 SAVEPOINT for this method to function correctly. 1347 1348 For documentation on SAVEPOINT 1349 transactions, please see :ref:`session_begin_nested`. 1350 1351 :return: the :class:`.SessionTransaction` object. Note that 1352 :class:`.SessionTransaction` acts as a context manager, allowing 1353 :meth:`.Session.begin_nested` to be used in a "with" block. 1354 See :ref:`session_begin_nested` for a usage example. 1355 1356 .. seealso:: 1357 1358 :ref:`session_begin_nested` 1359 1360 :ref:`pysqlite_serializable` - special workarounds required 1361 with the SQLite driver in order for SAVEPOINT to work 1362 correctly. 1363 1364 """ 1365 return self.begin(nested=True) 1366 1367 def rollback(self): 1368 """Rollback the current transaction in progress. 1369 1370 If no transaction is in progress, this method is a pass-through. 1371 1372 In :term:`1.x-style` use, this method rolls back the topmost 1373 database transaction if no nested transactions are in effect, or 1374 to the current nested transaction if one is in effect. 1375 1376 When 1377 :term:`2.0-style` use is in effect via the 1378 :paramref:`_orm.Session.future` flag, the method always rolls back 1379 the topmost database transaction, discarding any nested 1380 transactions that may be in progress. 1381 1382 .. seealso:: 1383 1384 :ref:`session_rollback` 1385 1386 :ref:`unitofwork_transaction` 1387 1388 """ 1389 if self._transaction is None: 1390 pass 1391 else: 1392 self._transaction.rollback(_to_root=self.future) 1393 1394 def commit(self): 1395 """Flush pending changes and commit the current transaction. 1396 1397 If no transaction is in progress, the method will first 1398 "autobegin" a new transaction and commit. 1399 1400 If :term:`1.x-style` use is in effect and there are currently 1401 SAVEPOINTs in progress via :meth:`_orm.Session.begin_nested`, 1402 the operation will release the current SAVEPOINT but not commit 1403 the outermost database transaction. 1404 1405 If :term:`2.0-style` use is in effect via the 1406 :paramref:`_orm.Session.future` flag, the outermost database 1407 transaction is committed unconditionally, automatically releasing any 1408 SAVEPOINTs in effect. 1409 1410 When using legacy "autocommit" mode, this method is only 1411 valid to call if a transaction is actually in progress, else 1412 an error is raised. Similarly, when using legacy "subtransactions", 1413 the method will instead close out the current "subtransaction", 1414 rather than the actual database transaction, if a transaction 1415 is in progress. 1416 1417 .. seealso:: 1418 1419 :ref:`session_committing` 1420 1421 :ref:`unitofwork_transaction` 1422 1423 """ 1424 if self._transaction is None: 1425 if not self._autobegin(): 1426 raise sa_exc.InvalidRequestError("No transaction is begun.") 1427 1428 self._transaction.commit(_to_root=self.future) 1429 1430 def prepare(self): 1431 """Prepare the current transaction in progress for two phase commit. 1432 1433 If no transaction is in progress, this method raises an 1434 :exc:`~sqlalchemy.exc.InvalidRequestError`. 1435 1436 Only root transactions of two phase sessions can be prepared. If the 1437 current transaction is not such, an 1438 :exc:`~sqlalchemy.exc.InvalidRequestError` is raised. 1439 1440 """ 1441 if self._transaction is None: 1442 if not self._autobegin(): 1443 raise sa_exc.InvalidRequestError("No transaction is begun.") 1444 1445 self._transaction.prepare() 1446 1447 def connection( 1448 self, 1449 bind_arguments=None, 1450 close_with_result=False, 1451 execution_options=None, 1452 **kw 1453 ): 1454 r"""Return a :class:`_engine.Connection` object corresponding to this 1455 :class:`.Session` object's transactional state. 1456 1457 If this :class:`.Session` is configured with ``autocommit=False``, 1458 either the :class:`_engine.Connection` corresponding to the current 1459 transaction is returned, or if no transaction is in progress, a new 1460 one is begun and the :class:`_engine.Connection` 1461 returned (note that no 1462 transactional state is established with the DBAPI until the first 1463 SQL statement is emitted). 1464 1465 Alternatively, if this :class:`.Session` is configured with 1466 ``autocommit=True``, an ad-hoc :class:`_engine.Connection` is returned 1467 using :meth:`_engine.Engine.connect` on the underlying 1468 :class:`_engine.Engine`. 1469 1470 Ambiguity in multi-bind or unbound :class:`.Session` objects can be 1471 resolved through any of the optional keyword arguments. This 1472 ultimately makes usage of the :meth:`.get_bind` method for resolution. 1473 1474 :param bind_arguments: dictionary of bind arguments. May include 1475 "mapper", "bind", "clause", other custom arguments that are passed 1476 to :meth:`.Session.get_bind`. 1477 1478 :param bind: 1479 deprecated; use bind_arguments 1480 1481 :param mapper: 1482 deprecated; use bind_arguments 1483 1484 :param clause: 1485 deprecated; use bind_arguments 1486 1487 :param close_with_result: Passed to :meth:`_engine.Engine.connect`, 1488 indicating the :class:`_engine.Connection` should be considered 1489 "single use", automatically closing when the first result set is 1490 closed. This flag only has an effect if this :class:`.Session` is 1491 configured with ``autocommit=True`` and does not already have a 1492 transaction in progress. 1493 1494 .. deprecated:: 1.4 this parameter is deprecated and will be removed 1495 in SQLAlchemy 2.0 1496 1497 :param execution_options: a dictionary of execution options that will 1498 be passed to :meth:`_engine.Connection.execution_options`, **when the 1499 connection is first procured only**. If the connection is already 1500 present within the :class:`.Session`, a warning is emitted and 1501 the arguments are ignored. 1502 1503 .. seealso:: 1504 1505 :ref:`session_transaction_isolation` 1506 1507 :param \**kw: 1508 deprecated; use bind_arguments 1509 1510 """ 1511 1512 if not bind_arguments: 1513 bind_arguments = kw 1514 1515 bind = bind_arguments.pop("bind", None) 1516 if bind is None: 1517 bind = self.get_bind(**bind_arguments) 1518 1519 return self._connection_for_bind( 1520 bind, 1521 close_with_result=close_with_result, 1522 execution_options=execution_options, 1523 ) 1524 1525 def _connection_for_bind(self, engine, execution_options=None, **kw): 1526 TransactionalContext._trans_ctx_check(self) 1527 1528 if self._transaction is not None or self._autobegin(): 1529 return self._transaction._connection_for_bind( 1530 engine, execution_options 1531 ) 1532 1533 assert self._transaction is None 1534 assert self.autocommit 1535 conn = engine.connect(**kw) 1536 if execution_options: 1537 conn = conn.execution_options(**execution_options) 1538 return conn 1539 1540 def execute( 1541 self, 1542 statement, 1543 params=None, 1544 execution_options=util.EMPTY_DICT, 1545 bind_arguments=None, 1546 _parent_execute_state=None, 1547 _add_event=None, 1548 **kw 1549 ): 1550 r"""Execute a SQL expression construct. 1551 1552 Returns a :class:`_engine.Result` object representing 1553 results of the statement execution. 1554 1555 E.g.:: 1556 1557 from sqlalchemy import select 1558 result = session.execute( 1559 select(User).where(User.id == 5) 1560 ) 1561 1562 The API contract of :meth:`_orm.Session.execute` is similar to that 1563 of :meth:`_future.Connection.execute`, the :term:`2.0 style` version 1564 of :class:`_future.Connection`. 1565 1566 .. versionchanged:: 1.4 the :meth:`_orm.Session.execute` method is 1567 now the primary point of ORM statement execution when using 1568 :term:`2.0 style` ORM usage. 1569 1570 :param statement: 1571 An executable statement (i.e. an :class:`.Executable` expression 1572 such as :func:`_expression.select`). 1573 1574 :param params: 1575 Optional dictionary, or list of dictionaries, containing 1576 bound parameter values. If a single dictionary, single-row 1577 execution occurs; if a list of dictionaries, an 1578 "executemany" will be invoked. The keys in each dictionary 1579 must correspond to parameter names present in the statement. 1580 1581 :param execution_options: optional dictionary of execution options, 1582 which will be associated with the statement execution. This 1583 dictionary can provide a subset of the options that are accepted 1584 by :meth:`_engine.Connection.execution_options`, and may also 1585 provide additional options understood only in an ORM context. 1586 1587 :param bind_arguments: dictionary of additional arguments to determine 1588 the bind. May include "mapper", "bind", or other custom arguments. 1589 Contents of this dictionary are passed to the 1590 :meth:`.Session.get_bind` method. 1591 1592 :param mapper: 1593 deprecated; use the bind_arguments dictionary 1594 1595 :param bind: 1596 deprecated; use the bind_arguments dictionary 1597 1598 :param \**kw: 1599 deprecated; use the bind_arguments dictionary 1600 1601 :return: a :class:`_engine.Result` object. 1602 1603 1604 """ 1605 statement = coercions.expect(roles.StatementRole, statement) 1606 1607 if kw: 1608 util.warn_deprecated_20( 1609 "Passing bind arguments to Session.execute() as keyword " 1610 "arguments is deprecated and will be removed SQLAlchemy 2.0. " 1611 "Please use the bind_arguments parameter." 1612 ) 1613 if not bind_arguments: 1614 bind_arguments = kw 1615 else: 1616 bind_arguments.update(kw) 1617 elif not bind_arguments: 1618 bind_arguments = {} 1619 1620 if ( 1621 statement._propagate_attrs.get("compile_state_plugin", None) 1622 == "orm" 1623 ): 1624 # note that even without "future" mode, we need 1625 compile_state_cls = CompileState._get_plugin_class_for_plugin( 1626 statement, "orm" 1627 ) 1628 else: 1629 compile_state_cls = None 1630 1631 execution_options = util.coerce_to_immutabledict(execution_options) 1632 1633 if compile_state_cls is not None: 1634 ( 1635 statement, 1636 execution_options, 1637 ) = compile_state_cls.orm_pre_session_exec( 1638 self, 1639 statement, 1640 params, 1641 execution_options, 1642 bind_arguments, 1643 _parent_execute_state is not None, 1644 ) 1645 else: 1646 bind_arguments.setdefault("clause", statement) 1647 execution_options = execution_options.union( 1648 {"future_result": True} 1649 ) 1650 1651 if _parent_execute_state: 1652 events_todo = _parent_execute_state._remaining_events() 1653 else: 1654 events_todo = self.dispatch.do_orm_execute 1655 if _add_event: 1656 events_todo = list(events_todo) + [_add_event] 1657 1658 if events_todo: 1659 orm_exec_state = ORMExecuteState( 1660 self, 1661 statement, 1662 params, 1663 execution_options, 1664 bind_arguments, 1665 compile_state_cls, 1666 events_todo, 1667 ) 1668 for idx, fn in enumerate(events_todo): 1669 orm_exec_state._starting_event_idx = idx 1670 result = fn(orm_exec_state) 1671 if result: 1672 return result 1673 1674 statement = orm_exec_state.statement 1675 execution_options = orm_exec_state.local_execution_options 1676 1677 bind = self.get_bind(**bind_arguments) 1678 1679 if self.autocommit: 1680 # legacy stuff, we can't use future_result w/ autocommit because 1681 # we rely upon close_with_result, also legacy. it's all 1682 # interrelated 1683 conn = self._connection_for_bind(bind, close_with_result=True) 1684 execution_options = execution_options.union( 1685 dict(future_result=False) 1686 ) 1687 else: 1688 conn = self._connection_for_bind(bind) 1689 result = conn._execute_20(statement, params or {}, execution_options) 1690 1691 if compile_state_cls: 1692 result = compile_state_cls.orm_setup_cursor_result( 1693 self, 1694 statement, 1695 params, 1696 execution_options, 1697 bind_arguments, 1698 result, 1699 ) 1700 1701 return result 1702 1703 def scalar( 1704 self, 1705 statement, 1706 params=None, 1707 execution_options=util.EMPTY_DICT, 1708 bind_arguments=None, 1709 **kw 1710 ): 1711 """Execute a statement and return a scalar result. 1712 1713 Usage and parameters are the same as that of 1714 :meth:`_orm.Session.execute`; the return result is a scalar Python 1715 value. 1716 1717 """ 1718 1719 return self.execute( 1720 statement, 1721 params=params, 1722 execution_options=execution_options, 1723 bind_arguments=bind_arguments, 1724 **kw 1725 ).scalar() 1726 1727 def scalars( 1728 self, 1729 statement, 1730 params=None, 1731 execution_options=util.EMPTY_DICT, 1732 bind_arguments=None, 1733 **kw 1734 ): 1735 """Execute a statement and return the results as scalars. 1736 1737 Usage and parameters are the same as that of 1738 :meth:`_orm.Session.execute`; the return result is a 1739 :class:`_result.ScalarResult` filtering object which 1740 will return single elements rather than :class:`_row.Row` objects. 1741 1742 :return: a :class:`_result.ScalarResult` object 1743 1744 .. versionadded:: 1.4.24 1745 1746 """ 1747 1748 return self.execute( 1749 statement, 1750 params=params, 1751 execution_options=execution_options, 1752 bind_arguments=bind_arguments, 1753 **kw 1754 ).scalars() 1755 1756 def close(self): 1757 """Close out the transactional resources and ORM objects used by this 1758 :class:`_orm.Session`. 1759 1760 This expunges all ORM objects associated with this 1761 :class:`_orm.Session`, ends any transaction in progress and 1762 :term:`releases` any :class:`_engine.Connection` objects which this 1763 :class:`_orm.Session` itself has checked out from associated 1764 :class:`_engine.Engine` objects. The operation then leaves the 1765 :class:`_orm.Session` in a state which it may be used again. 1766 1767 .. tip:: 1768 1769 The :meth:`_orm.Session.close` method **does not prevent the 1770 Session from being used again**. The :class:`_orm.Session` itself 1771 does not actually have a distinct "closed" state; it merely means 1772 the :class:`_orm.Session` will release all database connections 1773 and ORM objects. 1774 1775 .. versionchanged:: 1.4 The :meth:`.Session.close` method does not 1776 immediately create a new :class:`.SessionTransaction` object; 1777 instead, the new :class:`.SessionTransaction` is created only if 1778 the :class:`.Session` is used again for a database operation. 1779 1780 .. seealso:: 1781 1782 :ref:`session_closing` - detail on the semantics of 1783 :meth:`_orm.Session.close` 1784 1785 """ 1786 self._close_impl(invalidate=False) 1787 1788 def invalidate(self): 1789 """Close this Session, using connection invalidation. 1790 1791 This is a variant of :meth:`.Session.close` that will additionally 1792 ensure that the :meth:`_engine.Connection.invalidate` 1793 method will be called on each :class:`_engine.Connection` object 1794 that is currently in use for a transaction (typically there is only 1795 one connection unless the :class:`_orm.Session` is used with 1796 multiple engines). 1797 1798 This can be called when the database is known to be in a state where 1799 the connections are no longer safe to be used. 1800 1801 Below illustrates a scenario when using `gevent 1802 <https://www.gevent.org/>`_, which can produce ``Timeout`` exceptions 1803 that may mean the underlying connection should be discarded:: 1804 1805 import gevent 1806 1807 try: 1808 sess = Session() 1809 sess.add(User()) 1810 sess.commit() 1811 except gevent.Timeout: 1812 sess.invalidate() 1813 raise 1814 except: 1815 sess.rollback() 1816 raise 1817 1818 The method additionally does everything that :meth:`_orm.Session.close` 1819 does, including that all ORM objects are expunged. 1820 1821 """ 1822 self._close_impl(invalidate=True) 1823 1824 def _close_impl(self, invalidate): 1825 self.expunge_all() 1826 if self._transaction is not None: 1827 for transaction in self._transaction._iterate_self_and_parents(): 1828 transaction.close(invalidate) 1829 1830 def expunge_all(self): 1831 """Remove all object instances from this ``Session``. 1832 1833 This is equivalent to calling ``expunge(obj)`` on all objects in this 1834 ``Session``. 1835 1836 """ 1837 1838 all_states = self.identity_map.all_states() + list(self._new) 1839 self.identity_map._kill() 1840 self.identity_map = identity.WeakInstanceDict() 1841 self._new = {} 1842 self._deleted = {} 1843 1844 statelib.InstanceState._detach_states(all_states, self) 1845 1846 def _add_bind(self, key, bind): 1847 try: 1848 insp = inspect(key) 1849 except sa_exc.NoInspectionAvailable as err: 1850 if not isinstance(key, type): 1851 util.raise_( 1852 sa_exc.ArgumentError( 1853 "Not an acceptable bind target: %s" % key 1854 ), 1855 replace_context=err, 1856 ) 1857 else: 1858 self.__binds[key] = bind 1859 else: 1860 if insp.is_selectable: 1861 self.__binds[insp] = bind 1862 elif insp.is_mapper: 1863 self.__binds[insp.class_] = bind 1864 for _selectable in insp._all_tables: 1865 self.__binds[_selectable] = bind 1866 else: 1867 raise sa_exc.ArgumentError( 1868 "Not an acceptable bind target: %s" % key 1869 ) 1870 1871 def bind_mapper(self, mapper, bind): 1872 """Associate a :class:`_orm.Mapper` or arbitrary Python class with a 1873 "bind", e.g. an :class:`_engine.Engine` or 1874 :class:`_engine.Connection`. 1875 1876 The given entity is added to a lookup used by the 1877 :meth:`.Session.get_bind` method. 1878 1879 :param mapper: a :class:`_orm.Mapper` object, 1880 or an instance of a mapped 1881 class, or any Python class that is the base of a set of mapped 1882 classes. 1883 1884 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 1885 object. 1886 1887 .. seealso:: 1888 1889 :ref:`session_partitioning` 1890 1891 :paramref:`.Session.binds` 1892 1893 :meth:`.Session.bind_table` 1894 1895 1896 """ 1897 self._add_bind(mapper, bind) 1898 1899 def bind_table(self, table, bind): 1900 """Associate a :class:`_schema.Table` with a "bind", e.g. an 1901 :class:`_engine.Engine` 1902 or :class:`_engine.Connection`. 1903 1904 The given :class:`_schema.Table` is added to a lookup used by the 1905 :meth:`.Session.get_bind` method. 1906 1907 :param table: a :class:`_schema.Table` object, 1908 which is typically the target 1909 of an ORM mapping, or is present within a selectable that is 1910 mapped. 1911 1912 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 1913 object. 1914 1915 .. seealso:: 1916 1917 :ref:`session_partitioning` 1918 1919 :paramref:`.Session.binds` 1920 1921 :meth:`.Session.bind_mapper` 1922 1923 1924 """ 1925 self._add_bind(table, bind) 1926 1927 def get_bind( 1928 self, 1929 mapper=None, 1930 clause=None, 1931 bind=None, 1932 _sa_skip_events=None, 1933 _sa_skip_for_implicit_returning=False, 1934 ): 1935 """Return a "bind" to which this :class:`.Session` is bound. 1936 1937 The "bind" is usually an instance of :class:`_engine.Engine`, 1938 except in the case where the :class:`.Session` has been 1939 explicitly bound directly to a :class:`_engine.Connection`. 1940 1941 For a multiply-bound or unbound :class:`.Session`, the 1942 ``mapper`` or ``clause`` arguments are used to determine the 1943 appropriate bind to return. 1944 1945 Note that the "mapper" argument is usually present 1946 when :meth:`.Session.get_bind` is called via an ORM 1947 operation such as a :meth:`.Session.query`, each 1948 individual INSERT/UPDATE/DELETE operation within a 1949 :meth:`.Session.flush`, call, etc. 1950 1951 The order of resolution is: 1952 1953 1. if mapper given and :paramref:`.Session.binds` is present, 1954 locate a bind based first on the mapper in use, then 1955 on the mapped class in use, then on any base classes that are 1956 present in the ``__mro__`` of the mapped class, from more specific 1957 superclasses to more general. 1958 2. if clause given and ``Session.binds`` is present, 1959 locate a bind based on :class:`_schema.Table` objects 1960 found in the given clause present in ``Session.binds``. 1961 3. if ``Session.binds`` is present, return that. 1962 4. if clause given, attempt to return a bind 1963 linked to the :class:`_schema.MetaData` ultimately 1964 associated with the clause. 1965 5. if mapper given, attempt to return a bind 1966 linked to the :class:`_schema.MetaData` ultimately 1967 associated with the :class:`_schema.Table` or other 1968 selectable to which the mapper is mapped. 1969 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError` 1970 is raised. 1971 1972 Note that the :meth:`.Session.get_bind` method can be overridden on 1973 a user-defined subclass of :class:`.Session` to provide any kind 1974 of bind resolution scheme. See the example at 1975 :ref:`session_custom_partitioning`. 1976 1977 :param mapper: 1978 Optional :func:`.mapper` mapped class or instance of 1979 :class:`_orm.Mapper`. The bind can be derived from a 1980 :class:`_orm.Mapper` 1981 first by consulting the "binds" map associated with this 1982 :class:`.Session`, and secondly by consulting the 1983 :class:`_schema.MetaData` 1984 associated with the :class:`_schema.Table` to which the 1985 :class:`_orm.Mapper` 1986 is mapped for a bind. 1987 1988 :param clause: 1989 A :class:`_expression.ClauseElement` (i.e. 1990 :func:`_expression.select`, 1991 :func:`_expression.text`, 1992 etc.). If the ``mapper`` argument is not present or could not 1993 produce a bind, the given expression construct will be searched 1994 for a bound element, typically a :class:`_schema.Table` 1995 associated with 1996 bound :class:`_schema.MetaData`. 1997 1998 .. seealso:: 1999 2000 :ref:`session_partitioning` 2001 2002 :paramref:`.Session.binds` 2003 2004 :meth:`.Session.bind_mapper` 2005 2006 :meth:`.Session.bind_table` 2007 2008 """ 2009 2010 # this function is documented as a subclassing hook, so we have 2011 # to call this method even if the return is simple 2012 if bind: 2013 return bind 2014 elif not self.__binds and self.bind: 2015 # simplest and most common case, we have a bind and no 2016 # per-mapper/table binds, we're done 2017 return self.bind 2018 2019 # we don't have self.bind and either have self.__binds 2020 # or we don't have self.__binds (which is legacy). Look at the 2021 # mapper and the clause 2022 if mapper is clause is None: 2023 if self.bind: 2024 return self.bind 2025 else: 2026 raise sa_exc.UnboundExecutionError( 2027 "This session is not bound to a single Engine or " 2028 "Connection, and no context was provided to locate " 2029 "a binding." 2030 ) 2031 2032 # look more closely at the mapper. 2033 if mapper is not None: 2034 try: 2035 mapper = inspect(mapper) 2036 except sa_exc.NoInspectionAvailable as err: 2037 if isinstance(mapper, type): 2038 util.raise_( 2039 exc.UnmappedClassError(mapper), 2040 replace_context=err, 2041 ) 2042 else: 2043 raise 2044 2045 # match up the mapper or clause in the __binds 2046 if self.__binds: 2047 # matching mappers and selectables to entries in the 2048 # binds dictionary; supported use case. 2049 if mapper: 2050 for cls in mapper.class_.__mro__: 2051 if cls in self.__binds: 2052 return self.__binds[cls] 2053 if clause is None: 2054 clause = mapper.persist_selectable 2055 2056 if clause is not None: 2057 plugin_subject = clause._propagate_attrs.get( 2058 "plugin_subject", None 2059 ) 2060 2061 if plugin_subject is not None: 2062 for cls in plugin_subject.mapper.class_.__mro__: 2063 if cls in self.__binds: 2064 return self.__binds[cls] 2065 2066 for obj in visitors.iterate(clause): 2067 if obj in self.__binds: 2068 return self.__binds[obj] 2069 2070 # none of the __binds matched, but we have a fallback bind. 2071 # return that 2072 if self.bind: 2073 return self.bind 2074 2075 # now we are in legacy territory. looking for "bind" on tables 2076 # that are via bound metadata. this goes away in 2.0. 2077 2078 future_msg = "" 2079 future_code = "" 2080 2081 if mapper and clause is None: 2082 clause = mapper.persist_selectable 2083 2084 if clause is not None: 2085 if clause.bind: 2086 if self.future: 2087 future_msg = ( 2088 " A bind was located via legacy bound metadata, but " 2089 "since future=True is set on this Session, this " 2090 "bind is ignored." 2091 ) 2092 else: 2093 util.warn_deprecated_20( 2094 "This Session located a target engine via bound " 2095 "metadata; as this functionality will be removed in " 2096 "SQLAlchemy 2.0, an Engine object should be passed " 2097 "to the Session() constructor directly." 2098 ) 2099 return clause.bind 2100 2101 if mapper: 2102 if mapper.persist_selectable.bind: 2103 if self.future: 2104 future_msg = ( 2105 " A bind was located via legacy bound metadata, but " 2106 "since future=True is set on this Session, this " 2107 "bind is ignored." 2108 ) 2109 else: 2110 util.warn_deprecated_20( 2111 "This Session located a target engine via bound " 2112 "metadata; as this functionality will be removed in " 2113 "SQLAlchemy 2.0, an Engine object should be passed " 2114 "to the Session() constructor directly." 2115 ) 2116 return mapper.persist_selectable.bind 2117 2118 context = [] 2119 if mapper is not None: 2120 context.append("mapper %s" % mapper) 2121 if clause is not None: 2122 context.append("SQL expression") 2123 2124 raise sa_exc.UnboundExecutionError( 2125 "Could not locate a bind configured on %s or this Session.%s" 2126 % (", ".join(context), future_msg), 2127 code=future_code, 2128 ) 2129 2130 def query(self, *entities, **kwargs): 2131 """Return a new :class:`_query.Query` object corresponding to this 2132 :class:`_orm.Session`. 2133 2134 """ 2135 2136 return self._query_cls(entities, self, **kwargs) 2137 2138 def _identity_lookup( 2139 self, 2140 mapper, 2141 primary_key_identity, 2142 identity_token=None, 2143 passive=attributes.PASSIVE_OFF, 2144 lazy_loaded_from=None, 2145 ): 2146 """Locate an object in the identity map. 2147 2148 Given a primary key identity, constructs an identity key and then 2149 looks in the session's identity map. If present, the object may 2150 be run through unexpiration rules (e.g. load unloaded attributes, 2151 check if was deleted). 2152 2153 e.g.:: 2154 2155 obj = session._identity_lookup(inspect(SomeClass), (1, )) 2156 2157 :param mapper: mapper in use 2158 :param primary_key_identity: the primary key we are searching for, as 2159 a tuple. 2160 :param identity_token: identity token that should be used to create 2161 the identity key. Used as is, however overriding subclasses can 2162 repurpose this in order to interpret the value in a special way, 2163 such as if None then look among multiple target tokens. 2164 :param passive: passive load flag passed to 2165 :func:`.loading.get_from_identity`, which impacts the behavior if 2166 the object is found; the object may be validated and/or unexpired 2167 if the flag allows for SQL to be emitted. 2168 :param lazy_loaded_from: an :class:`.InstanceState` that is 2169 specifically asking for this identity as a related identity. Used 2170 for sharding schemes where there is a correspondence between an object 2171 and a related object being lazy-loaded (or otherwise 2172 relationship-loaded). 2173 2174 :return: None if the object is not found in the identity map, *or* 2175 if the object was unexpired and found to have been deleted. 2176 if passive flags disallow SQL and the object is expired, returns 2177 PASSIVE_NO_RESULT. In all other cases the instance is returned. 2178 2179 .. versionchanged:: 1.4.0 - the :meth:`.Session._identity_lookup` 2180 method was moved from :class:`_query.Query` to 2181 :class:`.Session`, to avoid having to instantiate the 2182 :class:`_query.Query` object. 2183 2184 2185 """ 2186 2187 key = mapper.identity_key_from_primary_key( 2188 primary_key_identity, identity_token=identity_token 2189 ) 2190 return loading.get_from_identity(self, mapper, key, passive) 2191 2192 @property 2193 @util.contextmanager 2194 def no_autoflush(self): 2195 """Return a context manager that disables autoflush. 2196 2197 e.g.:: 2198 2199 with session.no_autoflush: 2200 2201 some_object = SomeClass() 2202 session.add(some_object) 2203 # won't autoflush 2204 some_object.related_thing = session.query(SomeRelated).first() 2205 2206 Operations that proceed within the ``with:`` block 2207 will not be subject to flushes occurring upon query 2208 access. This is useful when initializing a series 2209 of objects which involve existing database queries, 2210 where the uncompleted object should not yet be flushed. 2211 2212 """ 2213 autoflush = self.autoflush 2214 self.autoflush = False 2215 try: 2216 yield self 2217 finally: 2218 self.autoflush = autoflush 2219 2220 def _autoflush(self): 2221 if self.autoflush and not self._flushing: 2222 try: 2223 self.flush() 2224 except sa_exc.StatementError as e: 2225 # note we are reraising StatementError as opposed to 2226 # raising FlushError with "chaining" to remain compatible 2227 # with code that catches StatementError, IntegrityError, 2228 # etc. 2229 e.add_detail( 2230 "raised as a result of Query-invoked autoflush; " 2231 "consider using a session.no_autoflush block if this " 2232 "flush is occurring prematurely" 2233 ) 2234 util.raise_(e, with_traceback=sys.exc_info()[2]) 2235 2236 def refresh(self, instance, attribute_names=None, with_for_update=None): 2237 """Expire and refresh attributes on the given instance. 2238 2239 The selected attributes will first be expired as they would when using 2240 :meth:`_orm.Session.expire`; then a SELECT statement will be issued to 2241 the database to refresh column-oriented attributes with the current 2242 value available in the current transaction. 2243 2244 :func:`_orm.relationship` oriented attributes will also be immediately 2245 loaded if they were already eagerly loaded on the object, using the 2246 same eager loading strategy that they were loaded with originally. 2247 Unloaded relationship attributes will remain unloaded, as will 2248 relationship attributes that were originally lazy loaded. 2249 2250 .. versionadded:: 1.4 - the :meth:`_orm.Session.refresh` method 2251 can also refresh eagerly loaded attributes. 2252 2253 .. tip:: 2254 2255 While the :meth:`_orm.Session.refresh` method is capable of 2256 refreshing both column and relationship oriented attributes, its 2257 primary focus is on refreshing of local column-oriented attributes 2258 on a single instance. For more open ended "refresh" functionality, 2259 including the ability to refresh the attributes on many objects at 2260 once while having explicit control over relationship loader 2261 strategies, use the 2262 :ref:`populate existing <orm_queryguide_populate_existing>` feature 2263 instead. 2264 2265 Note that a highly isolated transaction will return the same values as 2266 were previously read in that same transaction, regardless of changes 2267 in database state outside of that transaction. Refreshing 2268 attributes usually only makes sense at the start of a transaction 2269 where database rows have not yet been accessed. 2270 2271 :param attribute_names: optional. An iterable collection of 2272 string attribute names indicating a subset of attributes to 2273 be refreshed. 2274 2275 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 2276 should be used, or may be a dictionary containing flags to 2277 indicate a more specific set of FOR UPDATE flags for the SELECT; 2278 flags should match the parameters of 2279 :meth:`_query.Query.with_for_update`. 2280 Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 2281 2282 .. seealso:: 2283 2284 :ref:`session_expire` - introductory material 2285 2286 :meth:`.Session.expire` 2287 2288 :meth:`.Session.expire_all` 2289 2290 :ref:`orm_queryguide_populate_existing` - allows any ORM query 2291 to refresh objects as they would be loaded normally. 2292 2293 """ 2294 try: 2295 state = attributes.instance_state(instance) 2296 except exc.NO_STATE as err: 2297 util.raise_( 2298 exc.UnmappedInstanceError(instance), 2299 replace_context=err, 2300 ) 2301 2302 self._expire_state(state, attribute_names) 2303 2304 if with_for_update == {}: 2305 raise sa_exc.ArgumentError( 2306 "with_for_update should be the boolean value " 2307 "True, or a dictionary with options. " 2308 "A blank dictionary is ambiguous." 2309 ) 2310 2311 with_for_update = query.ForUpdateArg._from_argument(with_for_update) 2312 2313 stmt = sql.select(object_mapper(instance)) 2314 if ( 2315 loading.load_on_ident( 2316 self, 2317 stmt, 2318 state.key, 2319 refresh_state=state, 2320 with_for_update=with_for_update, 2321 only_load_props=attribute_names, 2322 ) 2323 is None 2324 ): 2325 raise sa_exc.InvalidRequestError( 2326 "Could not refresh instance '%s'" % instance_str(instance) 2327 ) 2328 2329 def expire_all(self): 2330 """Expires all persistent instances within this Session. 2331 2332 When any attributes on a persistent instance is next accessed, 2333 a query will be issued using the 2334 :class:`.Session` object's current transactional context in order to 2335 load all expired attributes for the given instance. Note that 2336 a highly isolated transaction will return the same values as were 2337 previously read in that same transaction, regardless of changes 2338 in database state outside of that transaction. 2339 2340 To expire individual objects and individual attributes 2341 on those objects, use :meth:`Session.expire`. 2342 2343 The :class:`.Session` object's default behavior is to 2344 expire all state whenever the :meth:`Session.rollback` 2345 or :meth:`Session.commit` methods are called, so that new 2346 state can be loaded for the new transaction. For this reason, 2347 calling :meth:`Session.expire_all` should not be needed when 2348 autocommit is ``False``, assuming the transaction is isolated. 2349 2350 .. seealso:: 2351 2352 :ref:`session_expire` - introductory material 2353 2354 :meth:`.Session.expire` 2355 2356 :meth:`.Session.refresh` 2357 2358 :meth:`_orm.Query.populate_existing` 2359 2360 """ 2361 for state in self.identity_map.all_states(): 2362 state._expire(state.dict, self.identity_map._modified) 2363 2364 def expire(self, instance, attribute_names=None): 2365 """Expire the attributes on an instance. 2366 2367 Marks the attributes of an instance as out of date. When an expired 2368 attribute is next accessed, a query will be issued to the 2369 :class:`.Session` object's current transactional context in order to 2370 load all expired attributes for the given instance. Note that 2371 a highly isolated transaction will return the same values as were 2372 previously read in that same transaction, regardless of changes 2373 in database state outside of that transaction. 2374 2375 To expire all objects in the :class:`.Session` simultaneously, 2376 use :meth:`Session.expire_all`. 2377 2378 The :class:`.Session` object's default behavior is to 2379 expire all state whenever the :meth:`Session.rollback` 2380 or :meth:`Session.commit` methods are called, so that new 2381 state can be loaded for the new transaction. For this reason, 2382 calling :meth:`Session.expire` only makes sense for the specific 2383 case that a non-ORM SQL statement was emitted in the current 2384 transaction. 2385 2386 :param instance: The instance to be refreshed. 2387 :param attribute_names: optional list of string attribute names 2388 indicating a subset of attributes to be expired. 2389 2390 .. seealso:: 2391 2392 :ref:`session_expire` - introductory material 2393 2394 :meth:`.Session.expire` 2395 2396 :meth:`.Session.refresh` 2397 2398 :meth:`_orm.Query.populate_existing` 2399 2400 """ 2401 try: 2402 state = attributes.instance_state(instance) 2403 except exc.NO_STATE as err: 2404 util.raise_( 2405 exc.UnmappedInstanceError(instance), 2406 replace_context=err, 2407 ) 2408 self._expire_state(state, attribute_names) 2409 2410 def _expire_state(self, state, attribute_names): 2411 self._validate_persistent(state) 2412 if attribute_names: 2413 state._expire_attributes(state.dict, attribute_names) 2414 else: 2415 # pre-fetch the full cascade since the expire is going to 2416 # remove associations 2417 cascaded = list( 2418 state.manager.mapper.cascade_iterator("refresh-expire", state) 2419 ) 2420 self._conditional_expire(state) 2421 for o, m, st_, dct_ in cascaded: 2422 self._conditional_expire(st_) 2423 2424 def _conditional_expire(self, state, autoflush=None): 2425 """Expire a state if persistent, else expunge if pending""" 2426 2427 if state.key: 2428 state._expire(state.dict, self.identity_map._modified) 2429 elif state in self._new: 2430 self._new.pop(state) 2431 state._detach(self) 2432 2433 def expunge(self, instance): 2434 """Remove the `instance` from this ``Session``. 2435 2436 This will free all internal references to the instance. Cascading 2437 will be applied according to the *expunge* cascade rule. 2438 2439 """ 2440 try: 2441 state = attributes.instance_state(instance) 2442 except exc.NO_STATE as err: 2443 util.raise_( 2444 exc.UnmappedInstanceError(instance), 2445 replace_context=err, 2446 ) 2447 if state.session_id is not self.hash_key: 2448 raise sa_exc.InvalidRequestError( 2449 "Instance %s is not present in this Session" % state_str(state) 2450 ) 2451 2452 cascaded = list( 2453 state.manager.mapper.cascade_iterator("expunge", state) 2454 ) 2455 self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded]) 2456 2457 def _expunge_states(self, states, to_transient=False): 2458 for state in states: 2459 if state in self._new: 2460 self._new.pop(state) 2461 elif self.identity_map.contains_state(state): 2462 self.identity_map.safe_discard(state) 2463 self._deleted.pop(state, None) 2464 elif self._transaction: 2465 # state is "detached" from being deleted, but still present 2466 # in the transaction snapshot 2467 self._transaction._deleted.pop(state, None) 2468 statelib.InstanceState._detach_states( 2469 states, self, to_transient=to_transient 2470 ) 2471 2472 def _register_persistent(self, states): 2473 """Register all persistent objects from a flush. 2474 2475 This is used both for pending objects moving to the persistent 2476 state as well as already persistent objects. 2477 2478 """ 2479 2480 pending_to_persistent = self.dispatch.pending_to_persistent or None 2481 for state in states: 2482 mapper = _state_mapper(state) 2483 2484 # prevent against last minute dereferences of the object 2485 obj = state.obj() 2486 if obj is not None: 2487 2488 instance_key = mapper._identity_key_from_state(state) 2489 2490 if ( 2491 _none_set.intersection(instance_key[1]) 2492 and not mapper.allow_partial_pks 2493 or _none_set.issuperset(instance_key[1]) 2494 ): 2495 raise exc.FlushError( 2496 "Instance %s has a NULL identity key. If this is an " 2497 "auto-generated value, check that the database table " 2498 "allows generation of new primary key values, and " 2499 "that the mapped Column object is configured to " 2500 "expect these generated values. Ensure also that " 2501 "this flush() is not occurring at an inappropriate " 2502 "time, such as within a load() event." 2503 % state_str(state) 2504 ) 2505 2506 if state.key is None: 2507 state.key = instance_key 2508 elif state.key != instance_key: 2509 # primary key switch. use safe_discard() in case another 2510 # state has already replaced this one in the identity 2511 # map (see test/orm/test_naturalpks.py ReversePKsTest) 2512 self.identity_map.safe_discard(state) 2513 if state in self._transaction._key_switches: 2514 orig_key = self._transaction._key_switches[state][0] 2515 else: 2516 orig_key = state.key 2517 self._transaction._key_switches[state] = ( 2518 orig_key, 2519 instance_key, 2520 ) 2521 state.key = instance_key 2522 2523 # there can be an existing state in the identity map 2524 # that is replaced when the primary keys of two instances 2525 # are swapped; see test/orm/test_naturalpks.py -> test_reverse 2526 old = self.identity_map.replace(state) 2527 if ( 2528 old is not None 2529 and mapper._identity_key_from_state(old) == instance_key 2530 and old.obj() is not None 2531 ): 2532 util.warn( 2533 "Identity map already had an identity for %s, " 2534 "replacing it with newly flushed object. Are there " 2535 "load operations occurring inside of an event handler " 2536 "within the flush?" % (instance_key,) 2537 ) 2538 state._orphaned_outside_of_session = False 2539 2540 statelib.InstanceState._commit_all_states( 2541 ((state, state.dict) for state in states), self.identity_map 2542 ) 2543 2544 self._register_altered(states) 2545 2546 if pending_to_persistent is not None: 2547 for state in states.intersection(self._new): 2548 pending_to_persistent(self, state) 2549 2550 # remove from new last, might be the last strong ref 2551 for state in set(states).intersection(self._new): 2552 self._new.pop(state) 2553 2554 def _register_altered(self, states): 2555 if self._transaction: 2556 for state in states: 2557 if state in self._new: 2558 self._transaction._new[state] = True 2559 else: 2560 self._transaction._dirty[state] = True 2561 2562 def _remove_newly_deleted(self, states): 2563 persistent_to_deleted = self.dispatch.persistent_to_deleted or None 2564 for state in states: 2565 if self._transaction: 2566 self._transaction._deleted[state] = True 2567 2568 if persistent_to_deleted is not None: 2569 # get a strong reference before we pop out of 2570 # self._deleted 2571 obj = state.obj() # noqa 2572 2573 self.identity_map.safe_discard(state) 2574 self._deleted.pop(state, None) 2575 state._deleted = True 2576 # can't call state._detach() here, because this state 2577 # is still in the transaction snapshot and needs to be 2578 # tracked as part of that 2579 if persistent_to_deleted is not None: 2580 persistent_to_deleted(self, state) 2581 2582 def add(self, instance, _warn=True): 2583 """Place an object in the ``Session``. 2584 2585 Its state will be persisted to the database on the next flush 2586 operation. 2587 2588 Repeated calls to ``add()`` will be ignored. The opposite of ``add()`` 2589 is ``expunge()``. 2590 2591 """ 2592 if _warn and self._warn_on_events: 2593 self._flush_warning("Session.add()") 2594 2595 try: 2596 state = attributes.instance_state(instance) 2597 except exc.NO_STATE as err: 2598 util.raise_( 2599 exc.UnmappedInstanceError(instance), 2600 replace_context=err, 2601 ) 2602 2603 self._save_or_update_state(state) 2604 2605 def add_all(self, instances): 2606 """Add the given collection of instances to this ``Session``.""" 2607 2608 if self._warn_on_events: 2609 self._flush_warning("Session.add_all()") 2610 2611 for instance in instances: 2612 self.add(instance, _warn=False) 2613 2614 def _save_or_update_state(self, state): 2615 state._orphaned_outside_of_session = False 2616 self._save_or_update_impl(state) 2617 2618 mapper = _state_mapper(state) 2619 for o, m, st_, dct_ in mapper.cascade_iterator( 2620 "save-update", state, halt_on=self._contains_state 2621 ): 2622 self._save_or_update_impl(st_) 2623 2624 def delete(self, instance): 2625 """Mark an instance as deleted. 2626 2627 The database delete operation occurs upon ``flush()``. 2628 2629 """ 2630 if self._warn_on_events: 2631 self._flush_warning("Session.delete()") 2632 2633 try: 2634 state = attributes.instance_state(instance) 2635 except exc.NO_STATE as err: 2636 util.raise_( 2637 exc.UnmappedInstanceError(instance), 2638 replace_context=err, 2639 ) 2640 2641 self._delete_impl(state, instance, head=True) 2642 2643 def _delete_impl(self, state, obj, head): 2644 2645 if state.key is None: 2646 if head: 2647 raise sa_exc.InvalidRequestError( 2648 "Instance '%s' is not persisted" % state_str(state) 2649 ) 2650 else: 2651 return 2652 2653 to_attach = self._before_attach(state, obj) 2654 2655 if state in self._deleted: 2656 return 2657 2658 self.identity_map.add(state) 2659 2660 if to_attach: 2661 self._after_attach(state, obj) 2662 2663 if head: 2664 # grab the cascades before adding the item to the deleted list 2665 # so that autoflush does not delete the item 2666 # the strong reference to the instance itself is significant here 2667 cascade_states = list( 2668 state.manager.mapper.cascade_iterator("delete", state) 2669 ) 2670 2671 self._deleted[state] = obj 2672 2673 if head: 2674 for o, m, st_, dct_ in cascade_states: 2675 self._delete_impl(st_, o, False) 2676 2677 def get( 2678 self, 2679 entity, 2680 ident, 2681 options=None, 2682 populate_existing=False, 2683 with_for_update=None, 2684 identity_token=None, 2685 ): 2686 """Return an instance based on the given primary key identifier, 2687 or ``None`` if not found. 2688 2689 E.g.:: 2690 2691 my_user = session.get(User, 5) 2692 2693 some_object = session.get(VersionedFoo, (5, 10)) 2694 2695 some_object = session.get( 2696 VersionedFoo, 2697 {"id": 5, "version_id": 10} 2698 ) 2699 2700 .. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved 2701 from the now deprecated :meth:`_orm.Query.get` method. 2702 2703 :meth:`_orm.Session.get` is special in that it provides direct 2704 access to the identity map of the :class:`.Session`. 2705 If the given primary key identifier is present 2706 in the local identity map, the object is returned 2707 directly from this collection and no SQL is emitted, 2708 unless the object has been marked fully expired. 2709 If not present, 2710 a SELECT is performed in order to locate the object. 2711 2712 :meth:`_orm.Session.get` also will perform a check if 2713 the object is present in the identity map and 2714 marked as expired - a SELECT 2715 is emitted to refresh the object as well as to 2716 ensure that the row is still present. 2717 If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 2718 2719 :param entity: a mapped class or :class:`.Mapper` indicating the 2720 type of entity to be loaded. 2721 2722 :param ident: A scalar, tuple, or dictionary representing the 2723 primary key. For a composite (e.g. multiple column) primary key, 2724 a tuple or dictionary should be passed. 2725 2726 For a single-column primary key, the scalar calling form is typically 2727 the most expedient. If the primary key of a row is the value "5", 2728 the call looks like:: 2729 2730 my_object = session.get(SomeClass, 5) 2731 2732 The tuple form contains primary key values typically in 2733 the order in which they correspond to the mapped 2734 :class:`_schema.Table` 2735 object's primary key columns, or if the 2736 :paramref:`_orm.Mapper.primary_key` configuration parameter were 2737 used, in 2738 the order used for that parameter. For example, if the primary key 2739 of a row is represented by the integer 2740 digits "5, 10" the call would look like:: 2741 2742 my_object = session.get(SomeClass, (5, 10)) 2743 2744 The dictionary form should include as keys the mapped attribute names 2745 corresponding to each element of the primary key. If the mapped class 2746 has the attributes ``id``, ``version_id`` as the attributes which 2747 store the object's primary key value, the call would look like:: 2748 2749 my_object = session.get(SomeClass, {"id": 5, "version_id": 10}) 2750 2751 :param options: optional sequence of loader options which will be 2752 applied to the query, if one is emitted. 2753 2754 :param populate_existing: causes the method to unconditionally emit 2755 a SQL query and refresh the object with the newly loaded data, 2756 regardless of whether or not the object is already present. 2757 2758 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 2759 should be used, or may be a dictionary containing flags to 2760 indicate a more specific set of FOR UPDATE flags for the SELECT; 2761 flags should match the parameters of 2762 :meth:`_query.Query.with_for_update`. 2763 Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 2764 2765 :return: The object instance, or ``None``. 2766 2767 """ 2768 return self._get_impl( 2769 entity, 2770 ident, 2771 loading.load_on_pk_identity, 2772 options, 2773 populate_existing=populate_existing, 2774 with_for_update=with_for_update, 2775 identity_token=identity_token, 2776 ) 2777 2778 def _get_impl( 2779 self, 2780 entity, 2781 primary_key_identity, 2782 db_load_fn, 2783 options=None, 2784 populate_existing=False, 2785 with_for_update=None, 2786 identity_token=None, 2787 execution_options=None, 2788 ): 2789 2790 # convert composite types to individual args 2791 if hasattr(primary_key_identity, "__composite_values__"): 2792 primary_key_identity = primary_key_identity.__composite_values__() 2793 2794 mapper = inspect(entity) 2795 2796 if not mapper or not mapper.is_mapper: 2797 raise sa_exc.ArgumentError( 2798 "Expected mapped class or mapper, got: %r" % entity 2799 ) 2800 2801 is_dict = isinstance(primary_key_identity, dict) 2802 if not is_dict: 2803 primary_key_identity = util.to_list( 2804 primary_key_identity, default=(None,) 2805 ) 2806 2807 if len(primary_key_identity) != len(mapper.primary_key): 2808 raise sa_exc.InvalidRequestError( 2809 "Incorrect number of values in identifier to formulate " 2810 "primary key for session.get(); primary key columns " 2811 "are %s" % ",".join("'%s'" % c for c in mapper.primary_key) 2812 ) 2813 2814 if is_dict: 2815 try: 2816 primary_key_identity = list( 2817 primary_key_identity[prop.key] 2818 for prop in mapper._identity_key_props 2819 ) 2820 2821 except KeyError as err: 2822 util.raise_( 2823 sa_exc.InvalidRequestError( 2824 "Incorrect names of values in identifier to formulate " 2825 "primary key for session.get(); primary key attribute " 2826 "names are %s" 2827 % ",".join( 2828 "'%s'" % prop.key 2829 for prop in mapper._identity_key_props 2830 ) 2831 ), 2832 replace_context=err, 2833 ) 2834 2835 if ( 2836 not populate_existing 2837 and not mapper.always_refresh 2838 and with_for_update is None 2839 ): 2840 2841 instance = self._identity_lookup( 2842 mapper, primary_key_identity, identity_token=identity_token 2843 ) 2844 2845 if instance is not None: 2846 # reject calls for id in identity map but class 2847 # mismatch. 2848 if not issubclass(instance.__class__, mapper.class_): 2849 return None 2850 return instance 2851 elif instance is attributes.PASSIVE_CLASS_MISMATCH: 2852 return None 2853 2854 # set_label_style() not strictly necessary, however this will ensure 2855 # that tablename_colname style is used which at the moment is 2856 # asserted in a lot of unit tests :) 2857 2858 load_options = context.QueryContext.default_load_options 2859 2860 if populate_existing: 2861 load_options += {"_populate_existing": populate_existing} 2862 statement = sql.select(mapper).set_label_style( 2863 LABEL_STYLE_TABLENAME_PLUS_COL 2864 ) 2865 if with_for_update is not None: 2866 statement._for_update_arg = query.ForUpdateArg._from_argument( 2867 with_for_update 2868 ) 2869 2870 if options: 2871 statement = statement.options(*options) 2872 if execution_options: 2873 statement = statement.execution_options(**execution_options) 2874 return db_load_fn( 2875 self, 2876 statement, 2877 primary_key_identity, 2878 load_options=load_options, 2879 ) 2880 2881 def merge(self, instance, load=True, options=None): 2882 """Copy the state of a given instance into a corresponding instance 2883 within this :class:`.Session`. 2884 2885 :meth:`.Session.merge` examines the primary key attributes of the 2886 source instance, and attempts to reconcile it with an instance of the 2887 same primary key in the session. If not found locally, it attempts 2888 to load the object from the database based on primary key, and if 2889 none can be located, creates a new instance. The state of each 2890 attribute on the source instance is then copied to the target 2891 instance. The resulting target instance is then returned by the 2892 method; the original source instance is left unmodified, and 2893 un-associated with the :class:`.Session` if not already. 2894 2895 This operation cascades to associated instances if the association is 2896 mapped with ``cascade="merge"``. 2897 2898 See :ref:`unitofwork_merging` for a detailed discussion of merging. 2899 2900 .. versionchanged:: 1.1 - :meth:`.Session.merge` will now reconcile 2901 pending objects with overlapping primary keys in the same way 2902 as persistent. See :ref:`change_3601` for discussion. 2903 2904 :param instance: Instance to be merged. 2905 :param load: Boolean, when False, :meth:`.merge` switches into 2906 a "high performance" mode which causes it to forego emitting history 2907 events as well as all database access. This flag is used for 2908 cases such as transferring graphs of objects into a :class:`.Session` 2909 from a second level cache, or to transfer just-loaded objects 2910 into the :class:`.Session` owned by a worker thread or process 2911 without re-querying the database. 2912 2913 The ``load=False`` use case adds the caveat that the given 2914 object has to be in a "clean" state, that is, has no pending changes 2915 to be flushed - even if the incoming object is detached from any 2916 :class:`.Session`. This is so that when 2917 the merge operation populates local attributes and 2918 cascades to related objects and 2919 collections, the values can be "stamped" onto the 2920 target object as is, without generating any history or attribute 2921 events, and without the need to reconcile the incoming data with 2922 any existing related objects or collections that might not 2923 be loaded. The resulting objects from ``load=False`` are always 2924 produced as "clean", so it is only appropriate that the given objects 2925 should be "clean" as well, else this suggests a mis-use of the 2926 method. 2927 :param options: optional sequence of loader options which will be 2928 applied to the :meth:`_orm.Session.get` method when the merge 2929 operation loads the existing version of the object from the database. 2930 2931 .. versionadded:: 1.4.24 2932 2933 2934 .. seealso:: 2935 2936 :func:`.make_transient_to_detached` - provides for an alternative 2937 means of "merging" a single object into the :class:`.Session` 2938 2939 """ 2940 2941 if self._warn_on_events: 2942 self._flush_warning("Session.merge()") 2943 2944 _recursive = {} 2945 _resolve_conflict_map = {} 2946 2947 if load: 2948 # flush current contents if we expect to load data 2949 self._autoflush() 2950 2951 object_mapper(instance) # verify mapped 2952 autoflush = self.autoflush 2953 try: 2954 self.autoflush = False 2955 return self._merge( 2956 attributes.instance_state(instance), 2957 attributes.instance_dict(instance), 2958 load=load, 2959 options=options, 2960 _recursive=_recursive, 2961 _resolve_conflict_map=_resolve_conflict_map, 2962 ) 2963 finally: 2964 self.autoflush = autoflush 2965 2966 def _merge( 2967 self, 2968 state, 2969 state_dict, 2970 load=True, 2971 options=None, 2972 _recursive=None, 2973 _resolve_conflict_map=None, 2974 ): 2975 mapper = _state_mapper(state) 2976 if state in _recursive: 2977 return _recursive[state] 2978 2979 new_instance = False 2980 key = state.key 2981 2982 if key is None: 2983 if state in self._new: 2984 util.warn( 2985 "Instance %s is already pending in this Session yet is " 2986 "being merged again; this is probably not what you want " 2987 "to do" % state_str(state) 2988 ) 2989 2990 if not load: 2991 raise sa_exc.InvalidRequestError( 2992 "merge() with load=False option does not support " 2993 "objects transient (i.e. unpersisted) objects. flush() " 2994 "all changes on mapped instances before merging with " 2995 "load=False." 2996 ) 2997 key = mapper._identity_key_from_state(state) 2998 key_is_persistent = attributes.NEVER_SET not in key[1] and ( 2999 not _none_set.intersection(key[1]) 3000 or ( 3001 mapper.allow_partial_pks 3002 and not _none_set.issuperset(key[1]) 3003 ) 3004 ) 3005 else: 3006 key_is_persistent = True 3007 3008 if key in self.identity_map: 3009 try: 3010 merged = self.identity_map[key] 3011 except KeyError: 3012 # object was GC'ed right as we checked for it 3013 merged = None 3014 else: 3015 merged = None 3016 3017 if merged is None: 3018 if key_is_persistent and key in _resolve_conflict_map: 3019 merged = _resolve_conflict_map[key] 3020 3021 elif not load: 3022 if state.modified: 3023 raise sa_exc.InvalidRequestError( 3024 "merge() with load=False option does not support " 3025 "objects marked as 'dirty'. flush() all changes on " 3026 "mapped instances before merging with load=False." 3027 ) 3028 merged = mapper.class_manager.new_instance() 3029 merged_state = attributes.instance_state(merged) 3030 merged_state.key = key 3031 self._update_impl(merged_state) 3032 new_instance = True 3033 3034 elif key_is_persistent: 3035 merged = self.get( 3036 mapper.class_, 3037 key[1], 3038 identity_token=key[2], 3039 options=options, 3040 ) 3041 3042 if merged is None: 3043 merged = mapper.class_manager.new_instance() 3044 merged_state = attributes.instance_state(merged) 3045 merged_dict = attributes.instance_dict(merged) 3046 new_instance = True 3047 self._save_or_update_state(merged_state) 3048 else: 3049 merged_state = attributes.instance_state(merged) 3050 merged_dict = attributes.instance_dict(merged) 3051 3052 _recursive[state] = merged 3053 _resolve_conflict_map[key] = merged 3054 3055 # check that we didn't just pull the exact same 3056 # state out. 3057 if state is not merged_state: 3058 # version check if applicable 3059 if mapper.version_id_col is not None: 3060 existing_version = mapper._get_state_attr_by_column( 3061 state, 3062 state_dict, 3063 mapper.version_id_col, 3064 passive=attributes.PASSIVE_NO_INITIALIZE, 3065 ) 3066 3067 merged_version = mapper._get_state_attr_by_column( 3068 merged_state, 3069 merged_dict, 3070 mapper.version_id_col, 3071 passive=attributes.PASSIVE_NO_INITIALIZE, 3072 ) 3073 3074 if ( 3075 existing_version is not attributes.PASSIVE_NO_RESULT 3076 and merged_version is not attributes.PASSIVE_NO_RESULT 3077 and existing_version != merged_version 3078 ): 3079 raise exc.StaleDataError( 3080 "Version id '%s' on merged state %s " 3081 "does not match existing version '%s'. " 3082 "Leave the version attribute unset when " 3083 "merging to update the most recent version." 3084 % ( 3085 existing_version, 3086 state_str(merged_state), 3087 merged_version, 3088 ) 3089 ) 3090 3091 merged_state.load_path = state.load_path 3092 merged_state.load_options = state.load_options 3093 3094 # since we are copying load_options, we need to copy 3095 # the callables_ that would have been generated by those 3096 # load_options. 3097 # assumes that the callables we put in state.callables_ 3098 # are not instance-specific (which they should not be) 3099 merged_state._copy_callables(state) 3100 3101 for prop in mapper.iterate_properties: 3102 prop.merge( 3103 self, 3104 state, 3105 state_dict, 3106 merged_state, 3107 merged_dict, 3108 load, 3109 _recursive, 3110 _resolve_conflict_map, 3111 ) 3112 3113 if not load: 3114 # remove any history 3115 merged_state._commit_all(merged_dict, self.identity_map) 3116 3117 if new_instance: 3118 merged_state.manager.dispatch.load(merged_state, None) 3119 return merged 3120 3121 def _validate_persistent(self, state): 3122 if not self.identity_map.contains_state(state): 3123 raise sa_exc.InvalidRequestError( 3124 "Instance '%s' is not persistent within this Session" 3125 % state_str(state) 3126 ) 3127 3128 def _save_impl(self, state): 3129 if state.key is not None: 3130 raise sa_exc.InvalidRequestError( 3131 "Object '%s' already has an identity - " 3132 "it can't be registered as pending" % state_str(state) 3133 ) 3134 3135 obj = state.obj() 3136 to_attach = self._before_attach(state, obj) 3137 if state not in self._new: 3138 self._new[state] = obj 3139 state.insert_order = len(self._new) 3140 if to_attach: 3141 self._after_attach(state, obj) 3142 3143 def _update_impl(self, state, revert_deletion=False): 3144 if state.key is None: 3145 raise sa_exc.InvalidRequestError( 3146 "Instance '%s' is not persisted" % state_str(state) 3147 ) 3148 3149 if state._deleted: 3150 if revert_deletion: 3151 if not state._attached: 3152 return 3153 del state._deleted 3154 else: 3155 raise sa_exc.InvalidRequestError( 3156 "Instance '%s' has been deleted. " 3157 "Use the make_transient() " 3158 "function to send this object back " 3159 "to the transient state." % state_str(state) 3160 ) 3161 3162 obj = state.obj() 3163 3164 # check for late gc 3165 if obj is None: 3166 return 3167 3168 to_attach = self._before_attach(state, obj) 3169 3170 self._deleted.pop(state, None) 3171 if revert_deletion: 3172 self.identity_map.replace(state) 3173 else: 3174 self.identity_map.add(state) 3175 3176 if to_attach: 3177 self._after_attach(state, obj) 3178 elif revert_deletion: 3179 self.dispatch.deleted_to_persistent(self, state) 3180 3181 def _save_or_update_impl(self, state): 3182 if state.key is None: 3183 self._save_impl(state) 3184 else: 3185 self._update_impl(state) 3186 3187 def enable_relationship_loading(self, obj): 3188 """Associate an object with this :class:`.Session` for related 3189 object loading. 3190 3191 .. warning:: 3192 3193 :meth:`.enable_relationship_loading` exists to serve special 3194 use cases and is not recommended for general use. 3195 3196 Accesses of attributes mapped with :func:`_orm.relationship` 3197 will attempt to load a value from the database using this 3198 :class:`.Session` as the source of connectivity. The values 3199 will be loaded based on foreign key and primary key values 3200 present on this object - if not present, then those relationships 3201 will be unavailable. 3202 3203 The object will be attached to this session, but will 3204 **not** participate in any persistence operations; its state 3205 for almost all purposes will remain either "transient" or 3206 "detached", except for the case of relationship loading. 3207 3208 Also note that backrefs will often not work as expected. 3209 Altering a relationship-bound attribute on the target object 3210 may not fire off a backref event, if the effective value 3211 is what was already loaded from a foreign-key-holding value. 3212 3213 The :meth:`.Session.enable_relationship_loading` method is 3214 similar to the ``load_on_pending`` flag on :func:`_orm.relationship`. 3215 Unlike that flag, :meth:`.Session.enable_relationship_loading` allows 3216 an object to remain transient while still being able to load 3217 related items. 3218 3219 To make a transient object associated with a :class:`.Session` 3220 via :meth:`.Session.enable_relationship_loading` pending, add 3221 it to the :class:`.Session` using :meth:`.Session.add` normally. 3222 If the object instead represents an existing identity in the database, 3223 it should be merged using :meth:`.Session.merge`. 3224 3225 :meth:`.Session.enable_relationship_loading` does not improve 3226 behavior when the ORM is used normally - object references should be 3227 constructed at the object level, not at the foreign key level, so 3228 that they are present in an ordinary way before flush() 3229 proceeds. This method is not intended for general use. 3230 3231 .. seealso:: 3232 3233 :paramref:`_orm.relationship.load_on_pending` - this flag 3234 allows per-relationship loading of many-to-ones on items that 3235 are pending. 3236 3237 :func:`.make_transient_to_detached` - allows for an object to 3238 be added to a :class:`.Session` without SQL emitted, which then 3239 will unexpire attributes on access. 3240 3241 """ 3242 try: 3243 state = attributes.instance_state(obj) 3244 except exc.NO_STATE as err: 3245 util.raise_( 3246 exc.UnmappedInstanceError(obj), 3247 replace_context=err, 3248 ) 3249 3250 to_attach = self._before_attach(state, obj) 3251 state._load_pending = True 3252 if to_attach: 3253 self._after_attach(state, obj) 3254 3255 def _before_attach(self, state, obj): 3256 self._autobegin() 3257 3258 if state.session_id == self.hash_key: 3259 return False 3260 3261 if state.session_id and state.session_id in _sessions: 3262 raise sa_exc.InvalidRequestError( 3263 "Object '%s' is already attached to session '%s' " 3264 "(this is '%s')" 3265 % (state_str(state), state.session_id, self.hash_key) 3266 ) 3267 3268 self.dispatch.before_attach(self, state) 3269 3270 return True 3271 3272 def _after_attach(self, state, obj): 3273 state.session_id = self.hash_key 3274 if state.modified and state._strong_obj is None: 3275 state._strong_obj = obj 3276 self.dispatch.after_attach(self, state) 3277 3278 if state.key: 3279 self.dispatch.detached_to_persistent(self, state) 3280 else: 3281 self.dispatch.transient_to_pending(self, state) 3282 3283 def __contains__(self, instance): 3284 """Return True if the instance is associated with this session. 3285 3286 The instance may be pending or persistent within the Session for a 3287 result of True. 3288 3289 """ 3290 try: 3291 state = attributes.instance_state(instance) 3292 except exc.NO_STATE as err: 3293 util.raise_( 3294 exc.UnmappedInstanceError(instance), 3295 replace_context=err, 3296 ) 3297 return self._contains_state(state) 3298 3299 def __iter__(self): 3300 """Iterate over all pending or persistent instances within this 3301 Session. 3302 3303 """ 3304 return iter( 3305 list(self._new.values()) + list(self.identity_map.values()) 3306 ) 3307 3308 def _contains_state(self, state): 3309 return state in self._new or self.identity_map.contains_state(state) 3310 3311 def flush(self, objects=None): 3312 """Flush all the object changes to the database. 3313 3314 Writes out all pending object creations, deletions and modifications 3315 to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are 3316 automatically ordered by the Session's unit of work dependency 3317 solver. 3318 3319 Database operations will be issued in the current transactional 3320 context and do not affect the state of the transaction, unless an 3321 error occurs, in which case the entire transaction is rolled back. 3322 You may flush() as often as you like within a transaction to move 3323 changes from Python to the database's transaction buffer. 3324 3325 For ``autocommit`` Sessions with no active manual transaction, flush() 3326 will create a transaction on the fly that surrounds the entire set of 3327 operations into the flush. 3328 3329 :param objects: Optional; restricts the flush operation to operate 3330 only on elements that are in the given collection. 3331 3332 This feature is for an extremely narrow set of use cases where 3333 particular objects may need to be operated upon before the 3334 full flush() occurs. It is not intended for general use. 3335 3336 """ 3337 3338 if self._flushing: 3339 raise sa_exc.InvalidRequestError("Session is already flushing") 3340 3341 if self._is_clean(): 3342 return 3343 try: 3344 self._flushing = True 3345 self._flush(objects) 3346 finally: 3347 self._flushing = False 3348 3349 def _flush_warning(self, method): 3350 util.warn( 3351 "Usage of the '%s' operation is not currently supported " 3352 "within the execution stage of the flush process. " 3353 "Results may not be consistent. Consider using alternative " 3354 "event listeners or connection-level operations instead." % method 3355 ) 3356 3357 def _is_clean(self): 3358 return ( 3359 not self.identity_map.check_modified() 3360 and not self._deleted 3361 and not self._new 3362 ) 3363 3364 def _flush(self, objects=None): 3365 3366 dirty = self._dirty_states 3367 if not dirty and not self._deleted and not self._new: 3368 self.identity_map._modified.clear() 3369 return 3370 3371 flush_context = UOWTransaction(self) 3372 3373 if self.dispatch.before_flush: 3374 self.dispatch.before_flush(self, flush_context, objects) 3375 # re-establish "dirty states" in case the listeners 3376 # added 3377 dirty = self._dirty_states 3378 3379 deleted = set(self._deleted) 3380 new = set(self._new) 3381 3382 dirty = set(dirty).difference(deleted) 3383 3384 # create the set of all objects we want to operate upon 3385 if objects: 3386 # specific list passed in 3387 objset = set() 3388 for o in objects: 3389 try: 3390 state = attributes.instance_state(o) 3391 3392 except exc.NO_STATE as err: 3393 util.raise_( 3394 exc.UnmappedInstanceError(o), 3395 replace_context=err, 3396 ) 3397 objset.add(state) 3398 else: 3399 objset = None 3400 3401 # store objects whose fate has been decided 3402 processed = set() 3403 3404 # put all saves/updates into the flush context. detect top-level 3405 # orphans and throw them into deleted. 3406 if objset: 3407 proc = new.union(dirty).intersection(objset).difference(deleted) 3408 else: 3409 proc = new.union(dirty).difference(deleted) 3410 3411 for state in proc: 3412 is_orphan = _state_mapper(state)._is_orphan(state) 3413 3414 is_persistent_orphan = is_orphan and state.has_identity 3415 3416 if ( 3417 is_orphan 3418 and not is_persistent_orphan 3419 and state._orphaned_outside_of_session 3420 ): 3421 self._expunge_states([state]) 3422 else: 3423 _reg = flush_context.register_object( 3424 state, isdelete=is_persistent_orphan 3425 ) 3426 assert _reg, "Failed to add object to the flush context!" 3427 processed.add(state) 3428 3429 # put all remaining deletes into the flush context. 3430 if objset: 3431 proc = deleted.intersection(objset).difference(processed) 3432 else: 3433 proc = deleted.difference(processed) 3434 for state in proc: 3435 _reg = flush_context.register_object(state, isdelete=True) 3436 assert _reg, "Failed to add object to the flush context!" 3437 3438 if not flush_context.has_work: 3439 return 3440 3441 flush_context.transaction = transaction = self.begin(_subtrans=True) 3442 try: 3443 self._warn_on_events = True 3444 try: 3445 flush_context.execute() 3446 finally: 3447 self._warn_on_events = False 3448 3449 self.dispatch.after_flush(self, flush_context) 3450 3451 flush_context.finalize_flush_changes() 3452 3453 if not objects and self.identity_map._modified: 3454 len_ = len(self.identity_map._modified) 3455 3456 statelib.InstanceState._commit_all_states( 3457 [ 3458 (state, state.dict) 3459 for state in self.identity_map._modified 3460 ], 3461 instance_dict=self.identity_map, 3462 ) 3463 util.warn( 3464 "Attribute history events accumulated on %d " 3465 "previously clean instances " 3466 "within inner-flush event handlers have been " 3467 "reset, and will not result in database updates. " 3468 "Consider using set_committed_value() within " 3469 "inner-flush event handlers to avoid this warning." % len_ 3470 ) 3471 3472 # useful assertions: 3473 # if not objects: 3474 # assert not self.identity_map._modified 3475 # else: 3476 # assert self.identity_map._modified == \ 3477 # self.identity_map._modified.difference(objects) 3478 3479 self.dispatch.after_flush_postexec(self, flush_context) 3480 3481 transaction.commit() 3482 3483 except: 3484 with util.safe_reraise(): 3485 transaction.rollback(_capture_exception=True) 3486 3487 def bulk_save_objects( 3488 self, 3489 objects, 3490 return_defaults=False, 3491 update_changed_only=True, 3492 preserve_order=True, 3493 ): 3494 """Perform a bulk save of the given list of objects. 3495 3496 The bulk save feature allows mapped objects to be used as the 3497 source of simple INSERT and UPDATE operations which can be more easily 3498 grouped together into higher performing "executemany" 3499 operations; the extraction of data from the objects is also performed 3500 using a lower-latency process that ignores whether or not attributes 3501 have actually been modified in the case of UPDATEs, and also ignores 3502 SQL expressions. 3503 3504 The objects as given are not added to the session and no additional 3505 state is established on them. If the 3506 :paramref:`_orm.Session.bulk_save_objects.return_defaults` flag is set, 3507 then server-generated primary key values will be assigned to the 3508 returned objects, but **not server side defaults**; this is a 3509 limitation in the implementation. If stateful objects are desired, 3510 please use the standard :meth:`_orm.Session.add_all` approach or 3511 as an alternative newer mass-insert features such as 3512 :ref:`orm_dml_returning_objects`. 3513 3514 .. warning:: 3515 3516 The bulk save feature allows for a lower-latency INSERT/UPDATE 3517 of rows at the expense of most other unit-of-work features. 3518 Features such as object management, relationship handling, 3519 and SQL clause support are **silently omitted** in favor of raw 3520 INSERT/UPDATES of records. 3521 3522 Please note that newer versions of SQLAlchemy are **greatly 3523 improving the efficiency** of the standard flush process. It is 3524 **strongly recommended** to not use the bulk methods as they 3525 represent a forking of SQLAlchemy's functionality and are slowly 3526 being moved into legacy status. New features such as 3527 :ref:`orm_dml_returning_objects` are both more efficient than 3528 the "bulk" methods and provide more predictable functionality. 3529 3530 **Please read the list of caveats at** 3531 :ref:`bulk_operations_caveats` **before using this method, and 3532 fully test and confirm the functionality of all code developed 3533 using these systems.** 3534 3535 :param objects: a sequence of mapped object instances. The mapped 3536 objects are persisted as is, and are **not** associated with the 3537 :class:`.Session` afterwards. 3538 3539 For each object, whether the object is sent as an INSERT or an 3540 UPDATE is dependent on the same rules used by the :class:`.Session` 3541 in traditional operation; if the object has the 3542 :attr:`.InstanceState.key` 3543 attribute set, then the object is assumed to be "detached" and 3544 will result in an UPDATE. Otherwise, an INSERT is used. 3545 3546 In the case of an UPDATE, statements are grouped based on which 3547 attributes have changed, and are thus to be the subject of each 3548 SET clause. If ``update_changed_only`` is False, then all 3549 attributes present within each object are applied to the UPDATE 3550 statement, which may help in allowing the statements to be grouped 3551 together into a larger executemany(), and will also reduce the 3552 overhead of checking history on attributes. 3553 3554 :param return_defaults: when True, rows that are missing values which 3555 generate defaults, namely integer primary key defaults and sequences, 3556 will be inserted **one at a time**, so that the primary key value 3557 is available. In particular this will allow joined-inheritance 3558 and other multi-table mappings to insert correctly without the need 3559 to provide primary key values ahead of time; however, 3560 :paramref:`.Session.bulk_save_objects.return_defaults` **greatly 3561 reduces the performance gains** of the method overall. It is strongly 3562 advised to please use the standard :meth:`_orm.Session.add_all` 3563 approach. 3564 3565 :param update_changed_only: when True, UPDATE statements are rendered 3566 based on those attributes in each state that have logged changes. 3567 When False, all attributes present are rendered into the SET clause 3568 with the exception of primary key attributes. 3569 3570 :param preserve_order: when True, the order of inserts and updates 3571 matches exactly the order in which the objects are given. When 3572 False, common types of objects are grouped into inserts 3573 and updates, to allow for more batching opportunities. 3574 3575 .. versionadded:: 1.3 3576 3577 .. seealso:: 3578 3579 :ref:`bulk_operations` 3580 3581 :meth:`.Session.bulk_insert_mappings` 3582 3583 :meth:`.Session.bulk_update_mappings` 3584 3585 """ 3586 3587 def key(state): 3588 return (state.mapper, state.key is not None) 3589 3590 obj_states = (attributes.instance_state(obj) for obj in objects) 3591 if not preserve_order: 3592 obj_states = sorted(obj_states, key=key) 3593 3594 for (mapper, isupdate), states in itertools.groupby(obj_states, key): 3595 self._bulk_save_mappings( 3596 mapper, 3597 states, 3598 isupdate, 3599 True, 3600 return_defaults, 3601 update_changed_only, 3602 False, 3603 ) 3604 3605 def bulk_insert_mappings( 3606 self, mapper, mappings, return_defaults=False, render_nulls=False 3607 ): 3608 """Perform a bulk insert of the given list of mapping dictionaries. 3609 3610 The bulk insert feature allows plain Python dictionaries to be used as 3611 the source of simple INSERT operations which can be more easily 3612 grouped together into higher performing "executemany" 3613 operations. Using dictionaries, there is no "history" or session 3614 state management features in use, reducing latency when inserting 3615 large numbers of simple rows. 3616 3617 The values within the dictionaries as given are typically passed 3618 without modification into Core :meth:`_expression.Insert` constructs, 3619 after 3620 organizing the values within them across the tables to which 3621 the given mapper is mapped. 3622 3623 .. versionadded:: 1.0.0 3624 3625 .. warning:: 3626 3627 The bulk insert feature allows for a lower-latency INSERT 3628 of rows at the expense of most other unit-of-work features. 3629 Features such as object management, relationship handling, 3630 and SQL clause support are **silently omitted** in favor of raw 3631 INSERT of records. 3632 3633 Please note that newer versions of SQLAlchemy are **greatly 3634 improving the efficiency** of the standard flush process. It is 3635 **strongly recommended** to not use the bulk methods as they 3636 represent a forking of SQLAlchemy's functionality and are slowly 3637 being moved into legacy status. New features such as 3638 :ref:`orm_dml_returning_objects` are both more efficient than 3639 the "bulk" methods and provide more predictable functionality. 3640 3641 **Please read the list of caveats at** 3642 :ref:`bulk_operations_caveats` **before using this method, and 3643 fully test and confirm the functionality of all code developed 3644 using these systems.** 3645 3646 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 3647 object, 3648 representing the single kind of object represented within the mapping 3649 list. 3650 3651 :param mappings: a sequence of dictionaries, each one containing the 3652 state of the mapped row to be inserted, in terms of the attribute 3653 names on the mapped class. If the mapping refers to multiple tables, 3654 such as a joined-inheritance mapping, each dictionary must contain all 3655 keys to be populated into all tables. 3656 3657 :param return_defaults: when True, rows that are missing values which 3658 generate defaults, namely integer primary key defaults and sequences, 3659 will be inserted **one at a time**, so that the primary key value 3660 is available. In particular this will allow joined-inheritance 3661 and other multi-table mappings to insert correctly without the need 3662 to provide primary 3663 key values ahead of time; however, 3664 :paramref:`.Session.bulk_insert_mappings.return_defaults` 3665 **greatly reduces the performance gains** of the method overall. 3666 If the rows 3667 to be inserted only refer to a single table, then there is no 3668 reason this flag should be set as the returned default information 3669 is not used. 3670 3671 :param render_nulls: When True, a value of ``None`` will result 3672 in a NULL value being included in the INSERT statement, rather 3673 than the column being omitted from the INSERT. This allows all 3674 the rows being INSERTed to have the identical set of columns which 3675 allows the full set of rows to be batched to the DBAPI. Normally, 3676 each column-set that contains a different combination of NULL values 3677 than the previous row must omit a different series of columns from 3678 the rendered INSERT statement, which means it must be emitted as a 3679 separate statement. By passing this flag, the full set of rows 3680 are guaranteed to be batchable into one batch; the cost however is 3681 that server-side defaults which are invoked by an omitted column will 3682 be skipped, so care must be taken to ensure that these are not 3683 necessary. 3684 3685 .. warning:: 3686 3687 When this flag is set, **server side default SQL values will 3688 not be invoked** for those columns that are inserted as NULL; 3689 the NULL value will be sent explicitly. Care must be taken 3690 to ensure that no server-side default functions need to be 3691 invoked for the operation as a whole. 3692 3693 .. versionadded:: 1.1 3694 3695 .. seealso:: 3696 3697 :ref:`bulk_operations` 3698 3699 :meth:`.Session.bulk_save_objects` 3700 3701 :meth:`.Session.bulk_update_mappings` 3702 3703 """ 3704 self._bulk_save_mappings( 3705 mapper, 3706 mappings, 3707 False, 3708 False, 3709 return_defaults, 3710 False, 3711 render_nulls, 3712 ) 3713 3714 def bulk_update_mappings(self, mapper, mappings): 3715 """Perform a bulk update of the given list of mapping dictionaries. 3716 3717 The bulk update feature allows plain Python dictionaries to be used as 3718 the source of simple UPDATE operations which can be more easily 3719 grouped together into higher performing "executemany" 3720 operations. Using dictionaries, there is no "history" or session 3721 state management features in use, reducing latency when updating 3722 large numbers of simple rows. 3723 3724 .. versionadded:: 1.0.0 3725 3726 .. warning:: 3727 3728 The bulk update feature allows for a lower-latency UPDATE 3729 of rows at the expense of most other unit-of-work features. 3730 Features such as object management, relationship handling, 3731 and SQL clause support are **silently omitted** in favor of raw 3732 UPDATES of records. 3733 3734 Please note that newer versions of SQLAlchemy are **greatly 3735 improving the efficiency** of the standard flush process. It is 3736 **strongly recommended** to not use the bulk methods as they 3737 represent a forking of SQLAlchemy's functionality and are slowly 3738 being moved into legacy status. New features such as 3739 :ref:`orm_dml_returning_objects` are both more efficient than 3740 the "bulk" methods and provide more predictable functionality. 3741 3742 **Please read the list of caveats at** 3743 :ref:`bulk_operations_caveats` **before using this method, and 3744 fully test and confirm the functionality of all code developed 3745 using these systems.** 3746 3747 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 3748 object, 3749 representing the single kind of object represented within the mapping 3750 list. 3751 3752 :param mappings: a sequence of dictionaries, each one containing the 3753 state of the mapped row to be updated, in terms of the attribute names 3754 on the mapped class. If the mapping refers to multiple tables, such 3755 as a joined-inheritance mapping, each dictionary may contain keys 3756 corresponding to all tables. All those keys which are present and 3757 are not part of the primary key are applied to the SET clause of the 3758 UPDATE statement; the primary key values, which are required, are 3759 applied to the WHERE clause. 3760 3761 3762 .. seealso:: 3763 3764 :ref:`bulk_operations` 3765 3766 :meth:`.Session.bulk_insert_mappings` 3767 3768 :meth:`.Session.bulk_save_objects` 3769 3770 """ 3771 self._bulk_save_mappings( 3772 mapper, mappings, True, False, False, False, False 3773 ) 3774 3775 def _bulk_save_mappings( 3776 self, 3777 mapper, 3778 mappings, 3779 isupdate, 3780 isstates, 3781 return_defaults, 3782 update_changed_only, 3783 render_nulls, 3784 ): 3785 mapper = _class_to_mapper(mapper) 3786 self._flushing = True 3787 3788 transaction = self.begin(_subtrans=True) 3789 try: 3790 if isupdate: 3791 persistence._bulk_update( 3792 mapper, 3793 mappings, 3794 transaction, 3795 isstates, 3796 update_changed_only, 3797 ) 3798 else: 3799 persistence._bulk_insert( 3800 mapper, 3801 mappings, 3802 transaction, 3803 isstates, 3804 return_defaults, 3805 render_nulls, 3806 ) 3807 transaction.commit() 3808 3809 except: 3810 with util.safe_reraise(): 3811 transaction.rollback(_capture_exception=True) 3812 finally: 3813 self._flushing = False 3814 3815 def is_modified(self, instance, include_collections=True): 3816 r"""Return ``True`` if the given instance has locally 3817 modified attributes. 3818 3819 This method retrieves the history for each instrumented 3820 attribute on the instance and performs a comparison of the current 3821 value to its previously committed value, if any. 3822 3823 It is in effect a more expensive and accurate 3824 version of checking for the given instance in the 3825 :attr:`.Session.dirty` collection; a full test for 3826 each attribute's net "dirty" status is performed. 3827 3828 E.g.:: 3829 3830 return session.is_modified(someobject) 3831 3832 A few caveats to this method apply: 3833 3834 * Instances present in the :attr:`.Session.dirty` collection may 3835 report ``False`` when tested with this method. This is because 3836 the object may have received change events via attribute mutation, 3837 thus placing it in :attr:`.Session.dirty`, but ultimately the state 3838 is the same as that loaded from the database, resulting in no net 3839 change here. 3840 * Scalar attributes may not have recorded the previously set 3841 value when a new value was applied, if the attribute was not loaded, 3842 or was expired, at the time the new value was received - in these 3843 cases, the attribute is assumed to have a change, even if there is 3844 ultimately no net change against its database value. SQLAlchemy in 3845 most cases does not need the "old" value when a set event occurs, so 3846 it skips the expense of a SQL call if the old value isn't present, 3847 based on the assumption that an UPDATE of the scalar value is 3848 usually needed, and in those few cases where it isn't, is less 3849 expensive on average than issuing a defensive SELECT. 3850 3851 The "old" value is fetched unconditionally upon set only if the 3852 attribute container has the ``active_history`` flag set to ``True``. 3853 This flag is set typically for primary key attributes and scalar 3854 object references that are not a simple many-to-one. To set this 3855 flag for any arbitrary mapped column, use the ``active_history`` 3856 argument with :func:`.column_property`. 3857 3858 :param instance: mapped instance to be tested for pending changes. 3859 :param include_collections: Indicates if multivalued collections 3860 should be included in the operation. Setting this to ``False`` is a 3861 way to detect only local-column based properties (i.e. scalar columns 3862 or many-to-one foreign keys) that would result in an UPDATE for this 3863 instance upon flush. 3864 3865 """ 3866 state = object_state(instance) 3867 3868 if not state.modified: 3869 return False 3870 3871 dict_ = state.dict 3872 3873 for attr in state.manager.attributes: 3874 if ( 3875 not include_collections 3876 and hasattr(attr.impl, "get_collection") 3877 ) or not hasattr(attr.impl, "get_history"): 3878 continue 3879 3880 (added, unchanged, deleted) = attr.impl.get_history( 3881 state, dict_, passive=attributes.NO_CHANGE 3882 ) 3883 3884 if added or deleted: 3885 return True 3886 else: 3887 return False 3888 3889 @property 3890 def is_active(self): 3891 """True if this :class:`.Session` not in "partial rollback" state. 3892 3893 .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins 3894 a new transaction immediately, so this attribute will be False 3895 when the :class:`_orm.Session` is first instantiated. 3896 3897 "partial rollback" state typically indicates that the flush process 3898 of the :class:`_orm.Session` has failed, and that the 3899 :meth:`_orm.Session.rollback` method must be emitted in order to 3900 fully roll back the transaction. 3901 3902 If this :class:`_orm.Session` is not in a transaction at all, the 3903 :class:`_orm.Session` will autobegin when it is first used, so in this 3904 case :attr:`_orm.Session.is_active` will return True. 3905 3906 Otherwise, if this :class:`_orm.Session` is within a transaction, 3907 and that transaction has not been rolled back internally, the 3908 :attr:`_orm.Session.is_active` will also return True. 3909 3910 .. seealso:: 3911 3912 :ref:`faq_session_rollback` 3913 3914 :meth:`_orm.Session.in_transaction` 3915 3916 """ 3917 if self.autocommit: 3918 return ( 3919 self._transaction is not None and self._transaction.is_active 3920 ) 3921 else: 3922 return self._transaction is None or self._transaction.is_active 3923 3924 identity_map = None 3925 """A mapping of object identities to objects themselves. 3926 3927 Iterating through ``Session.identity_map.values()`` provides 3928 access to the full set of persistent objects (i.e., those 3929 that have row identity) currently in the session. 3930 3931 .. seealso:: 3932 3933 :func:`.identity_key` - helper function to produce the keys used 3934 in this dictionary. 3935 3936 """ 3937 3938 @property 3939 def _dirty_states(self): 3940 """The set of all persistent states considered dirty. 3941 3942 This method returns all states that were modified including 3943 those that were possibly deleted. 3944 3945 """ 3946 return self.identity_map._dirty_states() 3947 3948 @property 3949 def dirty(self): 3950 """The set of all persistent instances considered dirty. 3951 3952 E.g.:: 3953 3954 some_mapped_object in session.dirty 3955 3956 Instances are considered dirty when they were modified but not 3957 deleted. 3958 3959 Note that this 'dirty' calculation is 'optimistic'; most 3960 attribute-setting or collection modification operations will 3961 mark an instance as 'dirty' and place it in this set, even if 3962 there is no net change to the attribute's value. At flush 3963 time, the value of each attribute is compared to its 3964 previously saved value, and if there's no net change, no SQL 3965 operation will occur (this is a more expensive operation so 3966 it's only done at flush time). 3967 3968 To check if an instance has actionable net changes to its 3969 attributes, use the :meth:`.Session.is_modified` method. 3970 3971 """ 3972 return util.IdentitySet( 3973 [ 3974 state.obj() 3975 for state in self._dirty_states 3976 if state not in self._deleted 3977 ] 3978 ) 3979 3980 @property 3981 def deleted(self): 3982 "The set of all instances marked as 'deleted' within this ``Session``" 3983 3984 return util.IdentitySet(list(self._deleted.values())) 3985 3986 @property 3987 def new(self): 3988 "The set of all instances marked as 'new' within this ``Session``." 3989 3990 return util.IdentitySet(list(self._new.values())) 3991 3992 3993class sessionmaker(_SessionClassMethods): 3994 """A configurable :class:`.Session` factory. 3995 3996 The :class:`.sessionmaker` factory generates new 3997 :class:`.Session` objects when called, creating them given 3998 the configurational arguments established here. 3999 4000 e.g.:: 4001 4002 from sqlalchemy import create_engine 4003 from sqlalchemy.orm import sessionmaker 4004 4005 # an Engine, which the Session will use for connection 4006 # resources 4007 engine = create_engine('postgresql://scott:tiger@localhost/') 4008 4009 Session = sessionmaker(engine) 4010 4011 with Session() as session: 4012 session.add(some_object) 4013 session.add(some_other_object) 4014 session.commit() 4015 4016 Context manager use is optional; otherwise, the returned 4017 :class:`_orm.Session` object may be closed explicitly via the 4018 :meth:`_orm.Session.close` method. Using a 4019 ``try:/finally:`` block is optional, however will ensure that the close 4020 takes place even if there are database errors:: 4021 4022 session = Session() 4023 try: 4024 session.add(some_object) 4025 session.add(some_other_object) 4026 session.commit() 4027 finally: 4028 session.close() 4029 4030 :class:`.sessionmaker` acts as a factory for :class:`_orm.Session` 4031 objects in the same way as an :class:`_engine.Engine` acts as a factory 4032 for :class:`_engine.Connection` objects. In this way it also includes 4033 a :meth:`_orm.sessionmaker.begin` method, that provides a context 4034 manager which both begins and commits a transaction, as well as closes 4035 out the :class:`_orm.Session` when complete, rolling back the transaction 4036 if any errors occur:: 4037 4038 Session = sessionmaker(engine) 4039 4040 with Session.begin() as session: 4041 session.add(some_object) 4042 session.add(some_other_object) 4043 # commits transaction, closes session 4044 4045 .. versionadded:: 1.4 4046 4047 When calling upon :class:`_orm.sessionmaker` to construct a 4048 :class:`_orm.Session`, keyword arguments may also be passed to the 4049 method; these arguments will override that of the globally configured 4050 parameters. Below we use a :class:`_orm.sessionmaker` bound to a certain 4051 :class:`_engine.Engine` to produce a :class:`_orm.Session` that is instead 4052 bound to a specific :class:`_engine.Connection` procured from that engine:: 4053 4054 Session = sessionmaker(engine) 4055 4056 # bind an individual session to a connection 4057 4058 with engine.connect() as connection: 4059 with Session(bind=connection) as session: 4060 # work with session 4061 4062 The class also includes a method :meth:`_orm.sessionmaker.configure`, which 4063 can be used to specify additional keyword arguments to the factory, which 4064 will take effect for subsequent :class:`.Session` objects generated. This 4065 is usually used to associate one or more :class:`_engine.Engine` objects 4066 with an existing 4067 :class:`.sessionmaker` factory before it is first used:: 4068 4069 # application starts, sessionmaker does not have 4070 # an engine bound yet 4071 Session = sessionmaker() 4072 4073 # ... later, when an engine URL is read from a configuration 4074 # file or other events allow the engine to be created 4075 engine = create_engine('sqlite:///foo.db') 4076 Session.configure(bind=engine) 4077 4078 sess = Session() 4079 # work with session 4080 4081 .. seealso:: 4082 4083 :ref:`session_getting` - introductory text on creating 4084 sessions using :class:`.sessionmaker`. 4085 4086 """ 4087 4088 def __init__( 4089 self, 4090 bind=None, 4091 class_=Session, 4092 autoflush=True, 4093 autocommit=False, 4094 expire_on_commit=True, 4095 info=None, 4096 **kw 4097 ): 4098 r"""Construct a new :class:`.sessionmaker`. 4099 4100 All arguments here except for ``class_`` correspond to arguments 4101 accepted by :class:`.Session` directly. See the 4102 :meth:`.Session.__init__` docstring for more details on parameters. 4103 4104 :param bind: a :class:`_engine.Engine` or other :class:`.Connectable` 4105 with 4106 which newly created :class:`.Session` objects will be associated. 4107 :param class\_: class to use in order to create new :class:`.Session` 4108 objects. Defaults to :class:`.Session`. 4109 :param autoflush: The autoflush setting to use with newly created 4110 :class:`.Session` objects. 4111 :param autocommit: The autocommit setting to use with newly created 4112 :class:`.Session` objects. 4113 :param expire_on_commit=True: the 4114 :paramref:`_orm.Session.expire_on_commit` setting to use 4115 with newly created :class:`.Session` objects. 4116 4117 :param info: optional dictionary of information that will be available 4118 via :attr:`.Session.info`. Note this dictionary is *updated*, not 4119 replaced, when the ``info`` parameter is specified to the specific 4120 :class:`.Session` construction operation. 4121 4122 :param \**kw: all other keyword arguments are passed to the 4123 constructor of newly created :class:`.Session` objects. 4124 4125 """ 4126 kw["bind"] = bind 4127 kw["autoflush"] = autoflush 4128 kw["autocommit"] = autocommit 4129 kw["expire_on_commit"] = expire_on_commit 4130 if info is not None: 4131 kw["info"] = info 4132 self.kw = kw 4133 # make our own subclass of the given class, so that 4134 # events can be associated with it specifically. 4135 self.class_ = type(class_.__name__, (class_,), {}) 4136 4137 def begin(self): 4138 """Produce a context manager that both provides a new 4139 :class:`_orm.Session` as well as a transaction that commits. 4140 4141 4142 e.g.:: 4143 4144 Session = sessionmaker(some_engine) 4145 4146 with Session.begin() as session: 4147 session.add(some_object) 4148 4149 # commits transaction, closes session 4150 4151 .. versionadded:: 1.4 4152 4153 4154 """ 4155 4156 session = self() 4157 return session._maker_context_manager() 4158 4159 def __call__(self, **local_kw): 4160 """Produce a new :class:`.Session` object using the configuration 4161 established in this :class:`.sessionmaker`. 4162 4163 In Python, the ``__call__`` method is invoked on an object when 4164 it is "called" in the same way as a function:: 4165 4166 Session = sessionmaker() 4167 session = Session() # invokes sessionmaker.__call__() 4168 4169 """ 4170 for k, v in self.kw.items(): 4171 if k == "info" and "info" in local_kw: 4172 d = v.copy() 4173 d.update(local_kw["info"]) 4174 local_kw["info"] = d 4175 else: 4176 local_kw.setdefault(k, v) 4177 return self.class_(**local_kw) 4178 4179 def configure(self, **new_kw): 4180 """(Re)configure the arguments for this sessionmaker. 4181 4182 e.g.:: 4183 4184 Session = sessionmaker() 4185 4186 Session.configure(bind=create_engine('sqlite://')) 4187 """ 4188 self.kw.update(new_kw) 4189 4190 def __repr__(self): 4191 return "%s(class_=%r, %s)" % ( 4192 self.__class__.__name__, 4193 self.class_.__name__, 4194 ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), 4195 ) 4196 4197 4198def close_all_sessions(): 4199 """Close all sessions in memory. 4200 4201 This function consults a global registry of all :class:`.Session` objects 4202 and calls :meth:`.Session.close` on them, which resets them to a clean 4203 state. 4204 4205 This function is not for general use but may be useful for test suites 4206 within the teardown scheme. 4207 4208 .. versionadded:: 1.3 4209 4210 """ 4211 4212 for sess in _sessions.values(): 4213 sess.close() 4214 4215 4216def make_transient(instance): 4217 """Alter the state of the given instance so that it is :term:`transient`. 4218 4219 .. note:: 4220 4221 :func:`.make_transient` is a special-case function for 4222 advanced use cases only. 4223 4224 The given mapped instance is assumed to be in the :term:`persistent` or 4225 :term:`detached` state. The function will remove its association with any 4226 :class:`.Session` as well as its :attr:`.InstanceState.identity`. The 4227 effect is that the object will behave as though it were newly constructed, 4228 except retaining any attribute / collection values that were loaded at the 4229 time of the call. The :attr:`.InstanceState.deleted` flag is also reset 4230 if this object had been deleted as a result of using 4231 :meth:`.Session.delete`. 4232 4233 .. warning:: 4234 4235 :func:`.make_transient` does **not** "unexpire" or otherwise eagerly 4236 load ORM-mapped attributes that are not currently loaded at the time 4237 the function is called. This includes attributes which: 4238 4239 * were expired via :meth:`.Session.expire` 4240 4241 * were expired as the natural effect of committing a session 4242 transaction, e.g. :meth:`.Session.commit` 4243 4244 * are normally :term:`lazy loaded` but are not currently loaded 4245 4246 * are "deferred" via :ref:`deferred` and are not yet loaded 4247 4248 * were not present in the query which loaded this object, such as that 4249 which is common in joined table inheritance and other scenarios. 4250 4251 After :func:`.make_transient` is called, unloaded attributes such 4252 as those above will normally resolve to the value ``None`` when 4253 accessed, or an empty collection for a collection-oriented attribute. 4254 As the object is transient and un-associated with any database 4255 identity, it will no longer retrieve these values. 4256 4257 .. seealso:: 4258 4259 :func:`.make_transient_to_detached` 4260 4261 """ 4262 state = attributes.instance_state(instance) 4263 s = _state_session(state) 4264 if s: 4265 s._expunge_states([state]) 4266 4267 # remove expired state 4268 state.expired_attributes.clear() 4269 4270 # remove deferred callables 4271 if state.callables: 4272 del state.callables 4273 4274 if state.key: 4275 del state.key 4276 if state._deleted: 4277 del state._deleted 4278 4279 4280def make_transient_to_detached(instance): 4281 """Make the given transient instance :term:`detached`. 4282 4283 .. note:: 4284 4285 :func:`.make_transient_to_detached` is a special-case function for 4286 advanced use cases only. 4287 4288 All attribute history on the given instance 4289 will be reset as though the instance were freshly loaded 4290 from a query. Missing attributes will be marked as expired. 4291 The primary key attributes of the object, which are required, will be made 4292 into the "key" of the instance. 4293 4294 The object can then be added to a session, or merged 4295 possibly with the load=False flag, at which point it will look 4296 as if it were loaded that way, without emitting SQL. 4297 4298 This is a special use case function that differs from a normal 4299 call to :meth:`.Session.merge` in that a given persistent state 4300 can be manufactured without any SQL calls. 4301 4302 .. seealso:: 4303 4304 :func:`.make_transient` 4305 4306 :meth:`.Session.enable_relationship_loading` 4307 4308 """ 4309 state = attributes.instance_state(instance) 4310 if state.session_id or state.key: 4311 raise sa_exc.InvalidRequestError("Given object must be transient") 4312 state.key = state.mapper._identity_key_from_state(state) 4313 if state._deleted: 4314 del state._deleted 4315 state._commit_all(state.dict) 4316 state._expire_attributes(state.dict, state.unloaded_expirable) 4317 4318 4319def object_session(instance): 4320 """Return the :class:`.Session` to which the given instance belongs. 4321 4322 This is essentially the same as the :attr:`.InstanceState.session` 4323 accessor. See that attribute for details. 4324 4325 """ 4326 4327 try: 4328 state = attributes.instance_state(instance) 4329 except exc.NO_STATE as err: 4330 util.raise_( 4331 exc.UnmappedInstanceError(instance), 4332 replace_context=err, 4333 ) 4334 else: 4335 return _state_session(state) 4336 4337 4338_new_sessionid = util.counter() 4339