Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# orm/session.py 

2# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: http://www.opensource.org/licenses/mit-license.php 

7"""Provides the Session class and related utilities.""" 

8 

9 

10import itertools 

11import sys 

12import weakref 

13 

14from . import attributes 

15from . import exc 

16from . import identity 

17from . import loading 

18from . import persistence 

19from . import query 

20from . import state as statelib 

21from .base import _class_to_mapper 

22from .base import _none_set 

23from .base import _state_mapper 

24from .base import instance_str 

25from .base import object_mapper 

26from .base import object_state 

27from .base import state_str 

28from .deprecated_interfaces import SessionExtension 

29from .unitofwork import UOWTransaction 

30from .. import engine 

31from .. import exc as sa_exc 

32from .. import sql 

33from .. import util 

34from ..inspection import inspect 

35from ..sql import expression 

36from ..sql import util as sql_util 

37 

38 

39__all__ = ["Session", "SessionTransaction", "SessionExtension", "sessionmaker"] 

40 

41_sessions = weakref.WeakValueDictionary() 

42"""Weak-referencing dictionary of :class:`.Session` objects. 

43""" 

44 

45 

46def _state_session(state): 

47 """Given an :class:`.InstanceState`, return the :class:`.Session` 

48 associated, if any. 

49 """ 

50 if state.session_id: 

51 try: 

52 return _sessions[state.session_id] 

53 except KeyError: 

54 pass 

55 return None 

56 

57 

58class _SessionClassMethods(object): 

59 """Class-level methods for :class:`.Session`, :class:`.sessionmaker`.""" 

60 

61 @classmethod 

62 @util.deprecated( 

63 "1.3", 

64 "The :meth:`.Session.close_all` method is deprecated and will be " 

65 "removed in a future release. Please refer to " 

66 ":func:`.session.close_all_sessions`.", 

67 ) 

68 def close_all(cls): 

69 """Close *all* sessions in memory.""" 

70 

71 close_all_sessions() 

72 

73 @classmethod 

74 @util.dependencies("sqlalchemy.orm.util") 

75 def identity_key(cls, orm_util, *args, **kwargs): 

76 """Return an identity key. 

77 

78 This is an alias of :func:`.util.identity_key`. 

79 

80 """ 

81 return orm_util.identity_key(*args, **kwargs) 

82 

83 @classmethod 

84 def object_session(cls, instance): 

85 """Return the :class:`.Session` to which an object belongs. 

86 

87 This is an alias of :func:`.object_session`. 

88 

89 """ 

90 

91 return object_session(instance) 

92 

93 

94ACTIVE = util.symbol("ACTIVE") 

95PREPARED = util.symbol("PREPARED") 

96COMMITTED = util.symbol("COMMITTED") 

97DEACTIVE = util.symbol("DEACTIVE") 

98CLOSED = util.symbol("CLOSED") 

99 

100 

101class SessionTransaction(object): 

102 """A :class:`.Session`-level transaction. 

103 

104 :class:`.SessionTransaction` is a mostly behind-the-scenes object 

105 not normally referenced directly by application code. It coordinates 

106 among multiple :class:`_engine.Connection` objects, maintaining a database 

107 transaction for each one individually, committing or rolling them 

108 back all at once. It also provides optional two-phase commit behavior 

109 which can augment this coordination operation. 

110 

111 The :attr:`.Session.transaction` attribute of :class:`.Session` 

112 refers to the current :class:`.SessionTransaction` object in use, if any. 

113 The :attr:`.SessionTransaction.parent` attribute refers to the parent 

114 :class:`.SessionTransaction` in the stack of :class:`.SessionTransaction` 

115 objects. If this attribute is ``None``, then this is the top of the stack. 

116 If non-``None``, then this :class:`.SessionTransaction` refers either 

117 to a so-called "subtransaction" or a "nested" transaction. A 

118 "subtransaction" is a scoping concept that demarcates an inner portion 

119 of the outermost "real" transaction. A nested transaction, which 

120 is indicated when the :attr:`.SessionTransaction.nested` 

121 attribute is also True, indicates that this :class:`.SessionTransaction` 

122 corresponds to a SAVEPOINT. 

123 

124 **Life Cycle** 

125 

126 A :class:`.SessionTransaction` is associated with a :class:`.Session` 

127 in its default mode of ``autocommit=False`` immediately, associated 

128 with no database connections. As the :class:`.Session` is called upon 

129 to emit SQL on behalf of various :class:`_engine.Engine` or 

130 :class:`_engine.Connection` 

131 objects, a corresponding :class:`_engine.Connection` and associated 

132 :class:`.Transaction` is added to a collection within the 

133 :class:`.SessionTransaction` object, becoming one of the 

134 connection/transaction pairs maintained by the 

135 :class:`.SessionTransaction`. The start of a :class:`.SessionTransaction` 

136 can be tracked using the :meth:`.SessionEvents.after_transaction_create` 

137 event. 

138 

139 The lifespan of the :class:`.SessionTransaction` ends when the 

140 :meth:`.Session.commit`, :meth:`.Session.rollback` or 

141 :meth:`.Session.close` methods are called. At this point, the 

142 :class:`.SessionTransaction` removes its association with its parent 

143 :class:`.Session`. A :class:`.Session` that is in ``autocommit=False`` 

144 mode will create a new :class:`.SessionTransaction` to replace it 

145 immediately, whereas a :class:`.Session` that's in ``autocommit=True`` 

146 mode will remain without a :class:`.SessionTransaction` until the 

147 :meth:`.Session.begin` method is called. The end of a 

148 :class:`.SessionTransaction` can be tracked using the 

149 :meth:`.SessionEvents.after_transaction_end` event. 

150 

151 **Nesting and Subtransactions** 

152 

153 Another detail of :class:`.SessionTransaction` behavior is that it is 

154 capable of "nesting". This means that the :meth:`.Session.begin` method 

155 can be called while an existing :class:`.SessionTransaction` is already 

156 present, producing a new :class:`.SessionTransaction` that temporarily 

157 replaces the parent :class:`.SessionTransaction`. When a 

158 :class:`.SessionTransaction` is produced as nested, it assigns itself to 

159 the :attr:`.Session.transaction` attribute, and it additionally will assign 

160 the previous :class:`.SessionTransaction` to its :attr:`.Session.parent` 

161 attribute. The behavior is effectively a 

162 stack, where :attr:`.Session.transaction` refers to the current head of 

163 the stack, and the :attr:`.SessionTransaction.parent` attribute allows 

164 traversal up the stack until :attr:`.SessionTransaction.parent` is 

165 ``None``, indicating the top of the stack. 

166 

167 When the scope of :class:`.SessionTransaction` is ended via 

168 :meth:`.Session.commit` or :meth:`.Session.rollback`, it restores its 

169 parent :class:`.SessionTransaction` back onto the 

170 :attr:`.Session.transaction` attribute. 

171 

172 The purpose of this stack is to allow nesting of 

173 :meth:`.Session.rollback` or :meth:`.Session.commit` calls in context 

174 with various flavors of :meth:`.Session.begin`. This nesting behavior 

175 applies to when :meth:`.Session.begin_nested` is used to emit a 

176 SAVEPOINT transaction, and is also used to produce a so-called 

177 "subtransaction" which allows a block of code to use a 

178 begin/rollback/commit sequence regardless of whether or not its enclosing 

179 code block has begun a transaction. The :meth:`.flush` method, whether 

180 called explicitly or via autoflush, is the primary consumer of the 

181 "subtransaction" feature, in that it wishes to guarantee that it works 

182 within in a transaction block regardless of whether or not the 

183 :class:`.Session` is in transactional mode when the method is called. 

184 

185 Note that the flush process that occurs within the "autoflush" feature 

186 as well as when the :meth:`.Session.flush` method is used **always** 

187 creates a :class:`.SessionTransaction` object. This object is normally 

188 a subtransaction, unless the :class:`.Session` is in autocommit mode 

189 and no transaction exists at all, in which case it's the outermost 

190 transaction. Any event-handling logic or other inspection logic 

191 needs to take into account whether a :class:`.SessionTransaction` 

192 is the outermost transaction, a subtransaction, or a "nested" / SAVEPOINT 

193 transaction. 

194 

195 .. seealso:: 

196 

197 :meth:`.Session.rollback` 

198 

199 :meth:`.Session.commit` 

200 

201 :meth:`.Session.begin` 

202 

203 :meth:`.Session.begin_nested` 

204 

205 :attr:`.Session.is_active` 

206 

207 :meth:`.SessionEvents.after_transaction_create` 

208 

209 :meth:`.SessionEvents.after_transaction_end` 

210 

211 :meth:`.SessionEvents.after_commit` 

212 

213 :meth:`.SessionEvents.after_rollback` 

214 

215 :meth:`.SessionEvents.after_soft_rollback` 

216 

217 """ 

218 

219 _rollback_exception = None 

220 

221 def __init__(self, session, parent=None, nested=False): 

222 self.session = session 

223 self._connections = {} 

224 self._parent = parent 

225 self.nested = nested 

226 self._state = ACTIVE 

227 if not parent and nested: 

228 raise sa_exc.InvalidRequestError( 

229 "Can't start a SAVEPOINT transaction when no existing " 

230 "transaction is in progress" 

231 ) 

232 

233 if self.session._enable_transaction_accounting: 

234 self._take_snapshot() 

235 

236 self.session.dispatch.after_transaction_create(self.session, self) 

237 

238 @property 

239 def parent(self): 

240 """The parent :class:`.SessionTransaction` of this 

241 :class:`.SessionTransaction`. 

242 

243 If this attribute is ``None``, indicates this 

244 :class:`.SessionTransaction` is at the top of the stack, and 

245 corresponds to a real "COMMIT"/"ROLLBACK" 

246 block. If non-``None``, then this is either a "subtransaction" 

247 or a "nested" / SAVEPOINT transaction. If the 

248 :attr:`.SessionTransaction.nested` attribute is ``True``, then 

249 this is a SAVEPOINT, and if ``False``, indicates this a subtransaction. 

250 

251 .. versionadded:: 1.0.16 - use ._parent for previous versions 

252 

253 """ 

254 return self._parent 

255 

256 nested = False 

257 """Indicates if this is a nested, or SAVEPOINT, transaction. 

258 

259 When :attr:`.SessionTransaction.nested` is True, it is expected 

260 that :attr:`.SessionTransaction.parent` will be True as well. 

261 

262 """ 

263 

264 @property 

265 def is_active(self): 

266 return self.session is not None and self._state is ACTIVE 

267 

268 def _assert_active( 

269 self, 

270 prepared_ok=False, 

271 rollback_ok=False, 

272 deactive_ok=False, 

273 closed_msg="This transaction is closed", 

274 ): 

275 if self._state is COMMITTED: 

276 raise sa_exc.InvalidRequestError( 

277 "This session is in 'committed' state; no further " 

278 "SQL can be emitted within this transaction." 

279 ) 

280 elif self._state is PREPARED: 

281 if not prepared_ok: 

282 raise sa_exc.InvalidRequestError( 

283 "This session is in 'prepared' state; no further " 

284 "SQL can be emitted within this transaction." 

285 ) 

286 elif self._state is DEACTIVE: 

287 if not deactive_ok and not rollback_ok: 

288 if self._rollback_exception: 

289 raise sa_exc.InvalidRequestError( 

290 "This Session's transaction has been rolled back " 

291 "due to a previous exception during flush." 

292 " To begin a new transaction with this Session, " 

293 "first issue Session.rollback()." 

294 " Original exception was: %s" 

295 % self._rollback_exception, 

296 code="7s2a", 

297 ) 

298 elif not deactive_ok: 

299 raise sa_exc.InvalidRequestError( 

300 "This session is in 'inactive' state, due to the " 

301 "SQL transaction being rolled back; no further " 

302 "SQL can be emitted within this transaction." 

303 ) 

304 elif self._state is CLOSED: 

305 raise sa_exc.ResourceClosedError(closed_msg) 

306 

307 @property 

308 def _is_transaction_boundary(self): 

309 return self.nested or not self._parent 

310 

311 def connection(self, bindkey, execution_options=None, **kwargs): 

312 self._assert_active() 

313 bind = self.session.get_bind(bindkey, **kwargs) 

314 return self._connection_for_bind(bind, execution_options) 

315 

316 def _begin(self, nested=False): 

317 self._assert_active() 

318 return SessionTransaction(self.session, self, nested=nested) 

319 

320 def _iterate_self_and_parents(self, upto=None): 

321 

322 current = self 

323 result = () 

324 while current: 

325 result += (current,) 

326 if current._parent is upto: 

327 break 

328 elif current._parent is None: 

329 raise sa_exc.InvalidRequestError( 

330 "Transaction %s is not on the active transaction list" 

331 % (upto) 

332 ) 

333 else: 

334 current = current._parent 

335 

336 return result 

337 

338 def _take_snapshot(self): 

339 if not self._is_transaction_boundary: 

340 self._new = self._parent._new 

341 self._deleted = self._parent._deleted 

342 self._dirty = self._parent._dirty 

343 self._key_switches = self._parent._key_switches 

344 return 

345 

346 if not self.session._flushing: 

347 self.session.flush() 

348 

349 self._new = weakref.WeakKeyDictionary() 

350 self._deleted = weakref.WeakKeyDictionary() 

351 self._dirty = weakref.WeakKeyDictionary() 

352 self._key_switches = weakref.WeakKeyDictionary() 

353 

354 def _restore_snapshot(self, dirty_only=False): 

355 """Restore the restoration state taken before a transaction began. 

356 

357 Corresponds to a rollback. 

358 

359 """ 

360 assert self._is_transaction_boundary 

361 

362 to_expunge = set(self._new).union(self.session._new) 

363 self.session._expunge_states(to_expunge, to_transient=True) 

364 

365 for s, (oldkey, newkey) in self._key_switches.items(): 

366 # we probably can do this conditionally based on 

367 # if we expunged or not, but safe_discard does that anyway 

368 self.session.identity_map.safe_discard(s) 

369 

370 # restore the old key 

371 s.key = oldkey 

372 

373 # now restore the object, but only if we didn't expunge 

374 if s not in to_expunge: 

375 self.session.identity_map.replace(s) 

376 

377 for s in set(self._deleted).union(self.session._deleted): 

378 self.session._update_impl(s, revert_deletion=True) 

379 

380 assert not self.session._deleted 

381 

382 for s in self.session.identity_map.all_states(): 

383 if not dirty_only or s.modified or s in self._dirty: 

384 s._expire(s.dict, self.session.identity_map._modified) 

385 

386 def _remove_snapshot(self): 

387 """Remove the restoration state taken before a transaction began. 

388 

389 Corresponds to a commit. 

390 

391 """ 

392 assert self._is_transaction_boundary 

393 

394 if not self.nested and self.session.expire_on_commit: 

395 for s in self.session.identity_map.all_states(): 

396 s._expire(s.dict, self.session.identity_map._modified) 

397 

398 statelib.InstanceState._detach_states( 

399 list(self._deleted), self.session 

400 ) 

401 self._deleted.clear() 

402 elif self.nested: 

403 self._parent._new.update(self._new) 

404 self._parent._dirty.update(self._dirty) 

405 self._parent._deleted.update(self._deleted) 

406 self._parent._key_switches.update(self._key_switches) 

407 

408 def _connection_for_bind(self, bind, execution_options): 

409 self._assert_active() 

410 

411 if bind in self._connections: 

412 if execution_options: 

413 util.warn( 

414 "Connection is already established for the " 

415 "given bind; execution_options ignored" 

416 ) 

417 return self._connections[bind][0] 

418 

419 local_connect = False 

420 if self._parent: 

421 conn = self._parent._connection_for_bind(bind, execution_options) 

422 if not self.nested: 

423 return conn 

424 else: 

425 if isinstance(bind, engine.Connection): 

426 conn = bind 

427 if conn.engine in self._connections: 

428 raise sa_exc.InvalidRequestError( 

429 "Session already has a Connection associated for the " 

430 "given Connection's Engine" 

431 ) 

432 else: 

433 conn = bind._contextual_connect() 

434 local_connect = True 

435 

436 try: 

437 if execution_options: 

438 conn = conn.execution_options(**execution_options) 

439 

440 if self.session.twophase and self._parent is None: 

441 transaction = conn.begin_twophase() 

442 elif self.nested: 

443 transaction = conn.begin_nested() 

444 else: 

445 transaction = conn.begin() 

446 except: 

447 # connection will not not be associated with this Session; 

448 # close it immediately so that it isn't closed under GC 

449 if local_connect: 

450 conn.close() 

451 raise 

452 else: 

453 self._connections[conn] = self._connections[conn.engine] = ( 

454 conn, 

455 transaction, 

456 conn is not bind, 

457 ) 

458 self.session.dispatch.after_begin(self.session, self, conn) 

459 return conn 

460 

461 def prepare(self): 

462 if self._parent is not None or not self.session.twophase: 

463 raise sa_exc.InvalidRequestError( 

464 "'twophase' mode not enabled, or not root transaction; " 

465 "can't prepare." 

466 ) 

467 self._prepare_impl() 

468 

469 def _prepare_impl(self): 

470 self._assert_active() 

471 if self._parent is None or self.nested: 

472 self.session.dispatch.before_commit(self.session) 

473 

474 stx = self.session.transaction 

475 if stx is not self: 

476 for subtransaction in stx._iterate_self_and_parents(upto=self): 

477 subtransaction.commit() 

478 

479 if not self.session._flushing: 

480 for _flush_guard in range(100): 

481 if self.session._is_clean(): 

482 break 

483 self.session.flush() 

484 else: 

485 raise exc.FlushError( 

486 "Over 100 subsequent flushes have occurred within " 

487 "session.commit() - is an after_flush() hook " 

488 "creating new objects?" 

489 ) 

490 

491 if self._parent is None and self.session.twophase: 

492 try: 

493 for t in set(self._connections.values()): 

494 t[1].prepare() 

495 except: 

496 with util.safe_reraise(): 

497 self.rollback() 

498 

499 self._state = PREPARED 

500 

501 def commit(self): 

502 self._assert_active(prepared_ok=True) 

503 if self._state is not PREPARED: 

504 self._prepare_impl() 

505 

506 if self._parent is None or self.nested: 

507 for t in set(self._connections.values()): 

508 t[1].commit() 

509 

510 self._state = COMMITTED 

511 self.session.dispatch.after_commit(self.session) 

512 

513 if self.session._enable_transaction_accounting: 

514 self._remove_snapshot() 

515 

516 self.close() 

517 return self._parent 

518 

519 def rollback(self, _capture_exception=False): 

520 self._assert_active(prepared_ok=True, rollback_ok=True) 

521 

522 stx = self.session.transaction 

523 if stx is not self: 

524 for subtransaction in stx._iterate_self_and_parents(upto=self): 

525 subtransaction.close() 

526 

527 boundary = self 

528 rollback_err = None 

529 if self._state in (ACTIVE, PREPARED): 

530 for transaction in self._iterate_self_and_parents(): 

531 if transaction._parent is None or transaction.nested: 

532 try: 

533 for t in set(transaction._connections.values()): 

534 t[1].rollback() 

535 

536 transaction._state = DEACTIVE 

537 self.session.dispatch.after_rollback(self.session) 

538 except: 

539 rollback_err = sys.exc_info() 

540 finally: 

541 transaction._state = DEACTIVE 

542 if self.session._enable_transaction_accounting: 

543 transaction._restore_snapshot( 

544 dirty_only=transaction.nested 

545 ) 

546 boundary = transaction 

547 break 

548 else: 

549 transaction._state = DEACTIVE 

550 

551 sess = self.session 

552 

553 if ( 

554 not rollback_err 

555 and sess._enable_transaction_accounting 

556 and not sess._is_clean() 

557 ): 

558 

559 # if items were added, deleted, or mutated 

560 # here, we need to re-restore the snapshot 

561 util.warn( 

562 "Session's state has been changed on " 

563 "a non-active transaction - this state " 

564 "will be discarded." 

565 ) 

566 boundary._restore_snapshot(dirty_only=boundary.nested) 

567 

568 self.close() 

569 

570 if self._parent and _capture_exception: 

571 self._parent._rollback_exception = sys.exc_info()[1] 

572 

573 if rollback_err: 

574 util.raise_(rollback_err[1], with_traceback=rollback_err[2]) 

575 

576 sess.dispatch.after_soft_rollback(sess, self) 

577 

578 return self._parent 

579 

580 def close(self, invalidate=False): 

581 self.session.transaction = self._parent 

582 if self._parent is None: 

583 for connection, transaction, autoclose in set( 

584 self._connections.values() 

585 ): 

586 if invalidate: 

587 connection.invalidate() 

588 if autoclose: 

589 connection.close() 

590 else: 

591 transaction.close() 

592 

593 self._state = CLOSED 

594 self.session.dispatch.after_transaction_end(self.session, self) 

595 

596 if self._parent is None: 

597 if not self.session.autocommit: 

598 self.session.begin() 

599 self.session = None 

600 self._connections = None 

601 

602 def __enter__(self): 

603 return self 

604 

605 def __exit__(self, type_, value, traceback): 

606 self._assert_active(deactive_ok=True, prepared_ok=True) 

607 if self.session.transaction is None: 

608 return 

609 if type_ is None: 

610 try: 

611 self.commit() 

612 except: 

613 with util.safe_reraise(): 

614 self.rollback() 

615 else: 

616 self.rollback() 

617 

618 

619class Session(_SessionClassMethods): 

620 """Manages persistence operations for ORM-mapped objects. 

621 

622 The Session's usage paradigm is described at :doc:`/orm/session`. 

623 

624 

625 """ 

626 

627 public_methods = ( 

628 "__contains__", 

629 "__iter__", 

630 "add", 

631 "add_all", 

632 "begin", 

633 "begin_nested", 

634 "close", 

635 "commit", 

636 "connection", 

637 "delete", 

638 "execute", 

639 "expire", 

640 "expire_all", 

641 "expunge", 

642 "expunge_all", 

643 "flush", 

644 "get_bind", 

645 "is_modified", 

646 "bulk_save_objects", 

647 "bulk_insert_mappings", 

648 "bulk_update_mappings", 

649 "merge", 

650 "query", 

651 "refresh", 

652 "rollback", 

653 "scalar", 

654 ) 

655 

656 @util.deprecated_params( 

657 weak_identity_map=( 

658 "1.0", 

659 "The :paramref:`.Session.weak_identity_map` parameter as well as " 

660 "the strong-referencing identity map are deprecated, and will be " 

661 "removed in a future release. For the use case where objects " 

662 "present in a :class:`.Session` need to be automatically strong " 

663 "referenced, see the recipe at " 

664 ":ref:`session_referencing_behavior` for an event-based approach " 

665 "to maintaining strong identity references. ", 

666 ), 

667 _enable_transaction_accounting=( 

668 "0.7", 

669 "The :paramref:`.Session._enable_transaction_accounting` " 

670 "parameter is deprecated and will be removed in a future release.", 

671 ), 

672 extension=( 

673 "0.7", 

674 ":class:`.SessionExtension` is deprecated in favor of the " 

675 ":class:`.SessionEvents` listener interface. The " 

676 ":paramref:`.Session.extension` parameter will be " 

677 "removed in a future release.", 

678 ), 

679 ) 

680 def __init__( 

681 self, 

682 bind=None, 

683 autoflush=True, 

684 expire_on_commit=True, 

685 _enable_transaction_accounting=True, 

686 autocommit=False, 

687 twophase=False, 

688 weak_identity_map=None, 

689 binds=None, 

690 extension=None, 

691 enable_baked_queries=True, 

692 info=None, 

693 query_cls=None, 

694 ): 

695 r"""Construct a new Session. 

696 

697 See also the :class:`.sessionmaker` function which is used to 

698 generate a :class:`.Session`-producing callable with a given 

699 set of arguments. 

700 

701 :param autocommit: 

702 

703 .. warning:: 

704 

705 The autocommit flag is **not for general use**, and if it is 

706 used, queries should only be invoked within the span of a 

707 :meth:`.Session.begin` / :meth:`.Session.commit` pair. Executing 

708 queries outside of a demarcated transaction is a legacy mode 

709 of usage, and can in some cases lead to concurrent connection 

710 checkouts. 

711 

712 Defaults to ``False``. When ``True``, the 

713 :class:`.Session` does not keep a persistent transaction running, 

714 and will acquire connections from the engine on an as-needed basis, 

715 returning them immediately after their use. Flushes will begin and 

716 commit (or possibly rollback) their own transaction if no 

717 transaction is present. When using this mode, the 

718 :meth:`.Session.begin` method is used to explicitly start 

719 transactions. 

720 

721 .. seealso:: 

722 

723 :ref:`session_autocommit` 

724 

725 :param autoflush: When ``True``, all query operations will issue a 

726 :meth:`~.Session.flush` call to this ``Session`` before proceeding. 

727 This is a convenience feature so that :meth:`~.Session.flush` need 

728 not be called repeatedly in order for database queries to retrieve 

729 results. It's typical that ``autoflush`` is used in conjunction 

730 with ``autocommit=False``. In this scenario, explicit calls to 

731 :meth:`~.Session.flush` are rarely needed; you usually only need to 

732 call :meth:`~.Session.commit` (which flushes) to finalize changes. 

733 

734 :param bind: An optional :class:`_engine.Engine` or 

735 :class:`_engine.Connection` to 

736 which this ``Session`` should be bound. When specified, all SQL 

737 operations performed by this session will execute via this 

738 connectable. 

739 

740 :param binds: A dictionary which may specify any number of 

741 :class:`_engine.Engine` or :class:`_engine.Connection` 

742 objects as the source of 

743 connectivity for SQL operations on a per-entity basis. The keys 

744 of the dictionary consist of any series of mapped classes, 

745 arbitrary Python classes that are bases for mapped classes, 

746 :class:`_schema.Table` objects and :class:`_orm.Mapper` objects. 

747 The 

748 values of the dictionary are then instances of 

749 :class:`_engine.Engine` 

750 or less commonly :class:`_engine.Connection` objects. 

751 Operations which 

752 proceed relative to a particular mapped class will consult this 

753 dictionary for the closest matching entity in order to determine 

754 which :class:`_engine.Engine` should be used for a particular SQL 

755 operation. The complete heuristics for resolution are 

756 described at :meth:`.Session.get_bind`. Usage looks like:: 

757 

758 Session = sessionmaker(binds={ 

759 SomeMappedClass: create_engine('postgresql://engine1'), 

760 SomeDeclarativeBase: create_engine('postgresql://engine2'), 

761 some_mapper: create_engine('postgresql://engine3'), 

762 some_table: create_engine('postgresql://engine4'), 

763 }) 

764 

765 .. seealso:: 

766 

767 :ref:`session_partitioning` 

768 

769 :meth:`.Session.bind_mapper` 

770 

771 :meth:`.Session.bind_table` 

772 

773 :meth:`.Session.get_bind` 

774 

775 

776 :param \class_: Specify an alternate class other than 

777 ``sqlalchemy.orm.session.Session`` which should be used by the 

778 returned class. This is the only argument that is local to the 

779 :class:`.sessionmaker` function, and is not sent directly to the 

780 constructor for ``Session``. 

781 

782 :param enable_baked_queries: defaults to ``True``. A flag consumed 

783 by the :mod:`sqlalchemy.ext.baked` extension to determine if 

784 "baked queries" should be cached, as is the normal operation 

785 of this extension. When set to ``False``, all caching is disabled, 

786 including baked queries defined by the calling application as 

787 well as those used internally. Setting this flag to ``False`` 

788 can significantly reduce memory use, however will also degrade 

789 performance for those areas that make use of baked queries 

790 (such as relationship loaders). Additionally, baked query 

791 logic in the calling application or potentially within the ORM 

792 that may be malfunctioning due to cache key collisions or similar 

793 can be flagged by observing if this flag resolves the issue. 

794 

795 .. versionadded:: 1.2 

796 

797 :param _enable_transaction_accounting: A 

798 legacy-only flag which when ``False`` disables *all* 0.5-style 

799 object accounting on transaction boundaries. 

800 

801 :param expire_on_commit: Defaults to ``True``. When ``True``, all 

802 instances will be fully expired after each :meth:`~.commit`, 

803 so that all attribute/object access subsequent to a completed 

804 transaction will load from the most recent database state. 

805 

806 :param extension: An optional 

807 :class:`~.SessionExtension` instance, or a list 

808 of such instances, which will receive pre- and post- commit and 

809 flush events, as well as a post-rollback event. 

810 

811 :param info: optional dictionary of arbitrary data to be associated 

812 with this :class:`.Session`. Is available via the 

813 :attr:`.Session.info` attribute. Note the dictionary is copied at 

814 construction time so that modifications to the per- 

815 :class:`.Session` dictionary will be local to that 

816 :class:`.Session`. 

817 

818 .. versionadded:: 0.9.0 

819 

820 :param query_cls: Class which should be used to create new Query 

821 objects, as returned by the :meth:`~.Session.query` method. 

822 Defaults to :class:`_query.Query`. 

823 

824 :param twophase: When ``True``, all transactions will be started as 

825 a "two phase" transaction, i.e. using the "two phase" semantics 

826 of the database in use along with an XID. During a 

827 :meth:`~.commit`, after :meth:`~.flush` has been issued for all 

828 attached databases, the :meth:`~.TwoPhaseTransaction.prepare` 

829 method on each database's :class:`.TwoPhaseTransaction` will be 

830 called. This allows each database to roll back the entire 

831 transaction, before each transaction is committed. 

832 

833 :param weak_identity_map: Defaults to ``True`` - when set to 

834 ``False``, objects placed in the :class:`.Session` will be 

835 strongly referenced until explicitly removed or the 

836 :class:`.Session` is closed. 

837 

838 

839 """ 

840 

841 if weak_identity_map in (True, None): 

842 self._identity_cls = identity.WeakInstanceDict 

843 else: 

844 self._identity_cls = identity.StrongInstanceDict 

845 

846 self.identity_map = self._identity_cls() 

847 

848 self._new = {} # InstanceState->object, strong refs object 

849 self._deleted = {} # same 

850 self.bind = bind 

851 self.__binds = {} 

852 self._flushing = False 

853 self._warn_on_events = False 

854 self.transaction = None 

855 self.hash_key = _new_sessionid() 

856 self.autoflush = autoflush 

857 self.autocommit = autocommit 

858 self.expire_on_commit = expire_on_commit 

859 self.enable_baked_queries = enable_baked_queries 

860 self._enable_transaction_accounting = _enable_transaction_accounting 

861 

862 self.twophase = twophase 

863 self._query_cls = query_cls if query_cls else query.Query 

864 if info: 

865 self.info.update(info) 

866 

867 if extension: 

868 for ext in util.to_list(extension): 

869 SessionExtension._adapt_listener(self, ext) 

870 

871 if binds is not None: 

872 for key, bind in binds.items(): 

873 self._add_bind(key, bind) 

874 

875 if not self.autocommit: 

876 self.begin() 

877 _sessions[self.hash_key] = self 

878 

879 connection_callable = None 

880 

881 transaction = None 

882 """The current active or inactive :class:`.SessionTransaction`.""" 

883 

884 @util.memoized_property 

885 def info(self): 

886 """A user-modifiable dictionary. 

887 

888 The initial value of this dictionary can be populated using the 

889 ``info`` argument to the :class:`.Session` constructor or 

890 :class:`.sessionmaker` constructor or factory methods. The dictionary 

891 here is always local to this :class:`.Session` and can be modified 

892 independently of all other :class:`.Session` objects. 

893 

894 .. versionadded:: 0.9.0 

895 

896 """ 

897 return {} 

898 

899 def begin(self, subtransactions=False, nested=False): 

900 """Begin a transaction on this :class:`.Session`. 

901 

902 .. warning:: 

903 

904 The :meth:`.Session.begin` method is part of a larger pattern 

905 of use with the :class:`.Session` known as **autocommit mode**. 

906 This is essentially a **legacy mode of use** and is 

907 not necessary for new applications. The :class:`.Session` 

908 normally handles the work of "begin" transparently, which in 

909 turn relies upon the Python DBAPI to transparently "begin" 

910 transactions; there is **no need to explicitly begin transactions** 

911 when using modern :class:`.Session` programming patterns. 

912 In its default mode of ``autocommit=False``, the 

913 :class:`.Session` does all of its work within 

914 the context of a transaction, so as soon as you call 

915 :meth:`.Session.commit`, the next transaction is implicitly 

916 started when the next database operation is invoked. See 

917 :ref:`session_autocommit` for further background. 

918 

919 The method will raise an error if this :class:`.Session` is already 

920 inside of a transaction, unless 

921 :paramref:`~.Session.begin.subtransactions` or 

922 :paramref:`~.Session.begin.nested` are specified. A "subtransaction" 

923 is essentially a code embedding pattern that does not affect the 

924 transactional state of the database connection unless a rollback is 

925 emitted, in which case the whole transaction is rolled back. For 

926 documentation on subtransactions, please see 

927 :ref:`session_subtransactions`. 

928 

929 :param subtransactions: if True, indicates that this 

930 :meth:`~.Session.begin` can create a "subtransaction". 

931 

932 :param nested: if True, begins a SAVEPOINT transaction and is 

933 equivalent to calling :meth:`~.Session.begin_nested`. For 

934 documentation on SAVEPOINT transactions, please see 

935 :ref:`session_begin_nested`. 

936 

937 :return: the :class:`.SessionTransaction` object. Note that 

938 :class:`.SessionTransaction` 

939 acts as a Python context manager, allowing :meth:`.Session.begin` 

940 to be used in a "with" block. See :ref:`session_autocommit` for 

941 an example. 

942 

943 .. seealso:: 

944 

945 :ref:`session_autocommit` 

946 

947 :meth:`.Session.begin_nested` 

948 

949 

950 """ 

951 if self.transaction is not None: 

952 if subtransactions or nested: 

953 self.transaction = self.transaction._begin(nested=nested) 

954 else: 

955 raise sa_exc.InvalidRequestError( 

956 "A transaction is already begun. Use " 

957 "subtransactions=True to allow subtransactions." 

958 ) 

959 else: 

960 self.transaction = SessionTransaction(self, nested=nested) 

961 return self.transaction # needed for __enter__/__exit__ hook 

962 

963 def begin_nested(self): 

964 """Begin a "nested" transaction on this Session, e.g. SAVEPOINT. 

965 

966 The target database(s) and associated drivers must support SQL 

967 SAVEPOINT for this method to function correctly. 

968 

969 For documentation on SAVEPOINT 

970 transactions, please see :ref:`session_begin_nested`. 

971 

972 :return: the :class:`.SessionTransaction` object. Note that 

973 :class:`.SessionTransaction` acts as a context manager, allowing 

974 :meth:`.Session.begin_nested` to be used in a "with" block. 

975 See :ref:`session_begin_nested` for a usage example. 

976 

977 .. seealso:: 

978 

979 :ref:`session_begin_nested` 

980 

981 :ref:`pysqlite_serializable` - special workarounds required 

982 with the SQLite driver in order for SAVEPOINT to work 

983 correctly. 

984 

985 """ 

986 return self.begin(nested=True) 

987 

988 def rollback(self): 

989 """Rollback the current transaction in progress. 

990 

991 If no transaction is in progress, this method is a pass-through. 

992 

993 This method rolls back the current transaction or nested transaction 

994 regardless of subtransactions being in effect. All subtransactions up 

995 to the first real transaction are closed. Subtransactions occur when 

996 :meth:`.begin` is called multiple times. 

997 

998 .. seealso:: 

999 

1000 :ref:`session_rollback` 

1001 

1002 """ 

1003 if self.transaction is None: 

1004 pass 

1005 else: 

1006 self.transaction.rollback() 

1007 

1008 def commit(self): 

1009 """Flush pending changes and commit the current transaction. 

1010 

1011 If no transaction is in progress, this method raises an 

1012 :exc:`~sqlalchemy.exc.InvalidRequestError`. 

1013 

1014 By default, the :class:`.Session` also expires all database 

1015 loaded state on all ORM-managed attributes after transaction commit. 

1016 This so that subsequent operations load the most recent 

1017 data from the database. This behavior can be disabled using 

1018 the ``expire_on_commit=False`` option to :class:`.sessionmaker` or 

1019 the :class:`.Session` constructor. 

1020 

1021 If a subtransaction is in effect (which occurs when begin() is called 

1022 multiple times), the subtransaction will be closed, and the next call 

1023 to ``commit()`` will operate on the enclosing transaction. 

1024 

1025 When using the :class:`.Session` in its default mode of 

1026 ``autocommit=False``, a new transaction will 

1027 be begun immediately after the commit, but note that the newly begun 

1028 transaction does *not* use any connection resources until the first 

1029 SQL is actually emitted. 

1030 

1031 .. seealso:: 

1032 

1033 :ref:`session_committing` 

1034 

1035 """ 

1036 if self.transaction is None: 

1037 if not self.autocommit: 

1038 self.begin() 

1039 else: 

1040 raise sa_exc.InvalidRequestError("No transaction is begun.") 

1041 

1042 self.transaction.commit() 

1043 

1044 def prepare(self): 

1045 """Prepare the current transaction in progress for two phase commit. 

1046 

1047 If no transaction is in progress, this method raises an 

1048 :exc:`~sqlalchemy.exc.InvalidRequestError`. 

1049 

1050 Only root transactions of two phase sessions can be prepared. If the 

1051 current transaction is not such, an 

1052 :exc:`~sqlalchemy.exc.InvalidRequestError` is raised. 

1053 

1054 """ 

1055 if self.transaction is None: 

1056 if not self.autocommit: 

1057 self.begin() 

1058 else: 

1059 raise sa_exc.InvalidRequestError("No transaction is begun.") 

1060 

1061 self.transaction.prepare() 

1062 

1063 def connection( 

1064 self, 

1065 mapper=None, 

1066 clause=None, 

1067 bind=None, 

1068 close_with_result=False, 

1069 execution_options=None, 

1070 **kw 

1071 ): 

1072 r"""Return a :class:`_engine.Connection` object corresponding to this 

1073 :class:`.Session` object's transactional state. 

1074 

1075 If this :class:`.Session` is configured with ``autocommit=False``, 

1076 either the :class:`_engine.Connection` corresponding to the current 

1077 transaction is returned, or if no transaction is in progress, a new 

1078 one is begun and the :class:`_engine.Connection` 

1079 returned (note that no 

1080 transactional state is established with the DBAPI until the first 

1081 SQL statement is emitted). 

1082 

1083 Alternatively, if this :class:`.Session` is configured with 

1084 ``autocommit=True``, an ad-hoc :class:`_engine.Connection` is returned 

1085 using :meth:`_engine.Engine.connect` on the underlying 

1086 :class:`_engine.Engine`. 

1087 

1088 Ambiguity in multi-bind or unbound :class:`.Session` objects can be 

1089 resolved through any of the optional keyword arguments. This 

1090 ultimately makes usage of the :meth:`.get_bind` method for resolution. 

1091 

1092 :param bind: 

1093 Optional :class:`_engine.Engine` to be used as the bind. If 

1094 this engine is already involved in an ongoing transaction, 

1095 that connection will be used. This argument takes precedence 

1096 over ``mapper``, ``clause``. 

1097 

1098 :param mapper: 

1099 Optional :func:`.mapper` mapped class, used to identify 

1100 the appropriate bind. This argument takes precedence over 

1101 ``clause``. 

1102 

1103 :param clause: 

1104 A :class:`_expression.ClauseElement` (i.e. 

1105 :func:`_expression.select`, 

1106 :func:`_expression.text`, 

1107 etc.) which will be used to locate a bind, if a bind 

1108 cannot otherwise be identified. 

1109 

1110 :param close_with_result: Passed to :meth:`_engine.Engine.connect`, 

1111 indicating the :class:`_engine.Connection` should be considered 

1112 "single use", automatically closing when the first result set is 

1113 closed. This flag only has an effect if this :class:`.Session` is 

1114 configured with ``autocommit=True`` and does not already have a 

1115 transaction in progress. 

1116 

1117 :param execution_options: a dictionary of execution options that will 

1118 be passed to :meth:`_engine.Connection.execution_options`, **when the 

1119 connection is first procured only**. If the connection is already 

1120 present within the :class:`.Session`, a warning is emitted and 

1121 the arguments are ignored. 

1122 

1123 .. versionadded:: 0.9.9 

1124 

1125 .. seealso:: 

1126 

1127 :ref:`session_transaction_isolation` 

1128 

1129 :param \**kw: 

1130 Additional keyword arguments are sent to :meth:`get_bind()`, 

1131 allowing additional arguments to be passed to custom 

1132 implementations of :meth:`get_bind`. 

1133 

1134 """ 

1135 if bind is None: 

1136 bind = self.get_bind(mapper, clause=clause, **kw) 

1137 

1138 return self._connection_for_bind( 

1139 bind, 

1140 close_with_result=close_with_result, 

1141 execution_options=execution_options, 

1142 ) 

1143 

1144 def _connection_for_bind(self, engine, execution_options=None, **kw): 

1145 if self.transaction is not None: 

1146 return self.transaction._connection_for_bind( 

1147 engine, execution_options 

1148 ) 

1149 else: 

1150 conn = engine._contextual_connect(**kw) 

1151 if execution_options: 

1152 conn = conn.execution_options(**execution_options) 

1153 return conn 

1154 

1155 def execute(self, clause, params=None, mapper=None, bind=None, **kw): 

1156 r"""Execute a SQL expression construct or string statement within 

1157 the current transaction. 

1158 

1159 Returns a :class:`_engine.ResultProxy` representing 

1160 results of the statement execution, in the same manner as that of an 

1161 :class:`_engine.Engine` or 

1162 :class:`_engine.Connection`. 

1163 

1164 E.g.:: 

1165 

1166 result = session.execute( 

1167 user_table.select().where(user_table.c.id == 5) 

1168 ) 

1169 

1170 :meth:`~.Session.execute` accepts any executable clause construct, 

1171 such as :func:`_expression.select`, 

1172 :func:`_expression.insert`, 

1173 :func:`_expression.update`, 

1174 :func:`_expression.delete`, and 

1175 :func:`_expression.text`. Plain SQL strings can be passed 

1176 as well, which in the case of :meth:`.Session.execute` only 

1177 will be interpreted the same as if it were passed via a 

1178 :func:`_expression.text` construct. That is, the following usage:: 

1179 

1180 result = session.execute( 

1181 "SELECT * FROM user WHERE id=:param", 

1182 {"param":5} 

1183 ) 

1184 

1185 is equivalent to:: 

1186 

1187 from sqlalchemy import text 

1188 result = session.execute( 

1189 text("SELECT * FROM user WHERE id=:param"), 

1190 {"param":5} 

1191 ) 

1192 

1193 The second positional argument to :meth:`.Session.execute` is an 

1194 optional parameter set. Similar to that of 

1195 :meth:`_engine.Connection.execute`, whether this is passed as a single 

1196 dictionary, or a sequence of dictionaries, determines whether the DBAPI 

1197 cursor's ``execute()`` or ``executemany()`` is used to execute the 

1198 statement. An INSERT construct may be invoked for a single row:: 

1199 

1200 result = session.execute( 

1201 users.insert(), {"id": 7, "name": "somename"}) 

1202 

1203 or for multiple rows:: 

1204 

1205 result = session.execute(users.insert(), [ 

1206 {"id": 7, "name": "somename7"}, 

1207 {"id": 8, "name": "somename8"}, 

1208 {"id": 9, "name": "somename9"} 

1209 ]) 

1210 

1211 The statement is executed within the current transactional context of 

1212 this :class:`.Session`. The :class:`_engine.Connection` 

1213 which is used 

1214 to execute the statement can also be acquired directly by 

1215 calling the :meth:`.Session.connection` method. Both methods use 

1216 a rule-based resolution scheme in order to determine the 

1217 :class:`_engine.Connection`, 

1218 which in the average case is derived directly 

1219 from the "bind" of the :class:`.Session` itself, and in other cases 

1220 can be based on the :func:`.mapper` 

1221 and :class:`_schema.Table` objects passed to the method; see the 

1222 documentation for :meth:`.Session.get_bind` for a full description of 

1223 this scheme. 

1224 

1225 The :meth:`.Session.execute` method does *not* invoke autoflush. 

1226 

1227 The :class:`_engine.ResultProxy` returned by the 

1228 :meth:`.Session.execute` 

1229 method is returned with the "close_with_result" flag set to true; 

1230 the significance of this flag is that if this :class:`.Session` is 

1231 autocommitting and does not have a transaction-dedicated 

1232 :class:`_engine.Connection` available, a temporary 

1233 :class:`_engine.Connection` is 

1234 established for the statement execution, which is closed (meaning, 

1235 returned to the connection pool) when the :class:`_engine.ResultProxy` 

1236 has 

1237 consumed all available data. This applies *only* when the 

1238 :class:`.Session` is configured with autocommit=True and no 

1239 transaction has been started. 

1240 

1241 :param clause: 

1242 An executable statement (i.e. an :class:`.Executable` expression 

1243 such as :func:`_expression.select`) or string SQL statement 

1244 to be executed. 

1245 

1246 :param params: 

1247 Optional dictionary, or list of dictionaries, containing 

1248 bound parameter values. If a single dictionary, single-row 

1249 execution occurs; if a list of dictionaries, an 

1250 "executemany" will be invoked. The keys in each dictionary 

1251 must correspond to parameter names present in the statement. 

1252 

1253 :param mapper: 

1254 Optional :func:`.mapper` or mapped class, used to identify 

1255 the appropriate bind. This argument takes precedence over 

1256 ``clause`` when locating a bind. See :meth:`.Session.get_bind` 

1257 for more details. 

1258 

1259 :param bind: 

1260 Optional :class:`_engine.Engine` to be used as the bind. If 

1261 this engine is already involved in an ongoing transaction, 

1262 that connection will be used. This argument takes 

1263 precedence over ``mapper`` and ``clause`` when locating 

1264 a bind. 

1265 

1266 :param \**kw: 

1267 Additional keyword arguments are sent to :meth:`.Session.get_bind()` 

1268 to allow extensibility of "bind" schemes. 

1269 

1270 .. seealso:: 

1271 

1272 :ref:`sqlexpression_toplevel` - Tutorial on using Core SQL 

1273 constructs. 

1274 

1275 :ref:`connections_toplevel` - Further information on direct 

1276 statement execution. 

1277 

1278 :meth:`_engine.Connection.execute` 

1279 - core level statement execution 

1280 method, which is :meth:`.Session.execute` ultimately uses 

1281 in order to execute the statement. 

1282 

1283 """ 

1284 clause = expression._literal_as_text( 

1285 clause, allow_coercion_to_text=True 

1286 ) 

1287 

1288 if bind is None: 

1289 bind = self.get_bind(mapper, clause=clause, **kw) 

1290 

1291 return self._connection_for_bind(bind, close_with_result=True).execute( 

1292 clause, params or {} 

1293 ) 

1294 

1295 def scalar(self, clause, params=None, mapper=None, bind=None, **kw): 

1296 """Like :meth:`~.Session.execute` but return a scalar result.""" 

1297 

1298 return self.execute( 

1299 clause, params=params, mapper=mapper, bind=bind, **kw 

1300 ).scalar() 

1301 

1302 def close(self): 

1303 """Close this Session. 

1304 

1305 This clears all items and ends any transaction in progress. 

1306 

1307 If this session were created with ``autocommit=False``, a new 

1308 transaction is immediately begun. Note that this new transaction does 

1309 not use any connection resources until they are first needed. 

1310 

1311 """ 

1312 self._close_impl(invalidate=False) 

1313 

1314 def invalidate(self): 

1315 """Close this Session, using connection invalidation. 

1316 

1317 This is a variant of :meth:`.Session.close` that will additionally 

1318 ensure that the :meth:`_engine.Connection.invalidate` 

1319 method will be called 

1320 on all :class:`_engine.Connection` objects. This can be called when 

1321 the database is known to be in a state where the connections are 

1322 no longer safe to be used. 

1323 

1324 E.g.:: 

1325 

1326 try: 

1327 sess = Session() 

1328 sess.add(User()) 

1329 sess.commit() 

1330 except gevent.Timeout: 

1331 sess.invalidate() 

1332 raise 

1333 except: 

1334 sess.rollback() 

1335 raise 

1336 

1337 This clears all items and ends any transaction in progress. 

1338 

1339 If this session were created with ``autocommit=False``, a new 

1340 transaction is immediately begun. Note that this new transaction does 

1341 not use any connection resources until they are first needed. 

1342 

1343 .. versionadded:: 0.9.9 

1344 

1345 """ 

1346 self._close_impl(invalidate=True) 

1347 

1348 def _close_impl(self, invalidate): 

1349 self.expunge_all() 

1350 if self.transaction is not None: 

1351 for transaction in self.transaction._iterate_self_and_parents(): 

1352 transaction.close(invalidate) 

1353 

1354 def expunge_all(self): 

1355 """Remove all object instances from this ``Session``. 

1356 

1357 This is equivalent to calling ``expunge(obj)`` on all objects in this 

1358 ``Session``. 

1359 

1360 """ 

1361 

1362 all_states = self.identity_map.all_states() + list(self._new) 

1363 self.identity_map = self._identity_cls() 

1364 self._new = {} 

1365 self._deleted = {} 

1366 

1367 statelib.InstanceState._detach_states(all_states, self) 

1368 

1369 def _add_bind(self, key, bind): 

1370 try: 

1371 insp = inspect(key) 

1372 except sa_exc.NoInspectionAvailable as err: 

1373 if not isinstance(key, type): 

1374 util.raise_( 

1375 sa_exc.ArgumentError( 

1376 "Not an acceptable bind target: %s" % key 

1377 ), 

1378 replace_context=err, 

1379 ) 

1380 else: 

1381 self.__binds[key] = bind 

1382 else: 

1383 if insp.is_selectable: 

1384 self.__binds[insp] = bind 

1385 elif insp.is_mapper: 

1386 self.__binds[insp.class_] = bind 

1387 for selectable in insp._all_tables: 

1388 self.__binds[selectable] = bind 

1389 else: 

1390 raise sa_exc.ArgumentError( 

1391 "Not an acceptable bind target: %s" % key 

1392 ) 

1393 

1394 def bind_mapper(self, mapper, bind): 

1395 """Associate a :class:`_orm.Mapper` or arbitrary Python class with a 

1396 "bind", e.g. an :class:`_engine.Engine` or :class:`_engine.Connection` 

1397 . 

1398 

1399 The given entity is added to a lookup used by the 

1400 :meth:`.Session.get_bind` method. 

1401 

1402 :param mapper: a :class:`_orm.Mapper` object, 

1403 or an instance of a mapped 

1404 class, or any Python class that is the base of a set of mapped 

1405 classes. 

1406 

1407 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 

1408 object. 

1409 

1410 .. seealso:: 

1411 

1412 :ref:`session_partitioning` 

1413 

1414 :paramref:`.Session.binds` 

1415 

1416 :meth:`.Session.bind_table` 

1417 

1418 

1419 """ 

1420 self._add_bind(mapper, bind) 

1421 

1422 def bind_table(self, table, bind): 

1423 """Associate a :class:`_schema.Table` with a "bind", e.g. an 

1424 :class:`_engine.Engine` 

1425 or :class:`_engine.Connection`. 

1426 

1427 The given :class:`_schema.Table` is added to a lookup used by the 

1428 :meth:`.Session.get_bind` method. 

1429 

1430 :param table: a :class:`_schema.Table` object, 

1431 which is typically the target 

1432 of an ORM mapping, or is present within a selectable that is 

1433 mapped. 

1434 

1435 :param bind: an :class:`_engine.Engine` or :class:`_engine.Connection` 

1436 object. 

1437 

1438 .. seealso:: 

1439 

1440 :ref:`session_partitioning` 

1441 

1442 :paramref:`.Session.binds` 

1443 

1444 :meth:`.Session.bind_mapper` 

1445 

1446 

1447 """ 

1448 self._add_bind(table, bind) 

1449 

1450 def get_bind(self, mapper=None, clause=None): 

1451 """Return a "bind" to which this :class:`.Session` is bound. 

1452 

1453 The "bind" is usually an instance of :class:`_engine.Engine`, 

1454 except in the case where the :class:`.Session` has been 

1455 explicitly bound directly to a :class:`_engine.Connection`. 

1456 

1457 For a multiply-bound or unbound :class:`.Session`, the 

1458 ``mapper`` or ``clause`` arguments are used to determine the 

1459 appropriate bind to return. 

1460 

1461 Note that the "mapper" argument is usually present 

1462 when :meth:`.Session.get_bind` is called via an ORM 

1463 operation such as a :meth:`.Session.query`, each 

1464 individual INSERT/UPDATE/DELETE operation within a 

1465 :meth:`.Session.flush`, call, etc. 

1466 

1467 The order of resolution is: 

1468 

1469 1. if mapper given and session.binds is present, 

1470 locate a bind based first on the mapper in use, then 

1471 on the mapped class in use, then on any base classes that are 

1472 present in the ``__mro__`` of the mapped class, from more specific 

1473 superclasses to more general. 

1474 2. if clause given and session.binds is present, 

1475 locate a bind based on :class:`_schema.Table` objects 

1476 found in the given clause present in session.binds. 

1477 3. if session.bind is present, return that. 

1478 4. if clause given, attempt to return a bind 

1479 linked to the :class:`_schema.MetaData` ultimately 

1480 associated with the clause. 

1481 5. if mapper given, attempt to return a bind 

1482 linked to the :class:`_schema.MetaData` ultimately 

1483 associated with the :class:`_schema.Table` or other 

1484 selectable to which the mapper is mapped. 

1485 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError` 

1486 is raised. 

1487 

1488 Note that the :meth:`.Session.get_bind` method can be overridden on 

1489 a user-defined subclass of :class:`.Session` to provide any kind 

1490 of bind resolution scheme. See the example at 

1491 :ref:`session_custom_partitioning`. 

1492 

1493 :param mapper: 

1494 Optional :func:`.mapper` mapped class or instance of 

1495 :class:`_orm.Mapper`. The bind can be derived from a 

1496 :class:`_orm.Mapper` 

1497 first by consulting the "binds" map associated with this 

1498 :class:`.Session`, and secondly by consulting the 

1499 :class:`_schema.MetaData` 

1500 associated with the :class:`_schema.Table` to which the 

1501 :class:`_orm.Mapper` 

1502 is mapped for a bind. 

1503 

1504 :param clause: 

1505 A :class:`_expression.ClauseElement` (i.e. 

1506 :func:`_expression.select`, 

1507 :func:`_expression.text`, 

1508 etc.). If the ``mapper`` argument is not present or could not 

1509 produce a bind, the given expression construct will be searched 

1510 for a bound element, typically a :class:`_schema.Table` 

1511 associated with 

1512 bound :class:`_schema.MetaData`. 

1513 

1514 .. seealso:: 

1515 

1516 :ref:`session_partitioning` 

1517 

1518 :paramref:`.Session.binds` 

1519 

1520 :meth:`.Session.bind_mapper` 

1521 

1522 :meth:`.Session.bind_table` 

1523 

1524 """ 

1525 

1526 if mapper is clause is None: 

1527 if self.bind: 

1528 return self.bind 

1529 else: 

1530 raise sa_exc.UnboundExecutionError( 

1531 "This session is not bound to a single Engine or " 

1532 "Connection, and no context was provided to locate " 

1533 "a binding." 

1534 ) 

1535 

1536 if mapper is not None: 

1537 try: 

1538 mapper = inspect(mapper) 

1539 except sa_exc.NoInspectionAvailable as err: 

1540 if isinstance(mapper, type): 

1541 util.raise_( 

1542 exc.UnmappedClassError(mapper), replace_context=err, 

1543 ) 

1544 else: 

1545 raise 

1546 

1547 if self.__binds: 

1548 if mapper: 

1549 for cls in mapper.class_.__mro__: 

1550 if cls in self.__binds: 

1551 return self.__binds[cls] 

1552 if clause is None: 

1553 clause = mapper.persist_selectable 

1554 

1555 if clause is not None: 

1556 for t in sql_util.find_tables(clause, include_crud=True): 

1557 if t in self.__binds: 

1558 return self.__binds[t] 

1559 

1560 if self.bind: 

1561 return self.bind 

1562 

1563 if isinstance(clause, sql.expression.ClauseElement) and clause.bind: 

1564 return clause.bind 

1565 

1566 if mapper and mapper.persist_selectable.bind: 

1567 return mapper.persist_selectable.bind 

1568 

1569 context = [] 

1570 if mapper is not None: 

1571 context.append("mapper %s" % mapper) 

1572 if clause is not None: 

1573 context.append("SQL expression") 

1574 

1575 raise sa_exc.UnboundExecutionError( 

1576 "Could not locate a bind configured on %s or this Session" 

1577 % (", ".join(context)) 

1578 ) 

1579 

1580 def query(self, *entities, **kwargs): 

1581 """Return a new :class:`_query.Query` object corresponding to this 

1582 :class:`.Session`.""" 

1583 

1584 return self._query_cls(entities, self, **kwargs) 

1585 

1586 @property 

1587 @util.contextmanager 

1588 def no_autoflush(self): 

1589 """Return a context manager that disables autoflush. 

1590 

1591 e.g.:: 

1592 

1593 with session.no_autoflush: 

1594 

1595 some_object = SomeClass() 

1596 session.add(some_object) 

1597 # won't autoflush 

1598 some_object.related_thing = session.query(SomeRelated).first() 

1599 

1600 Operations that proceed within the ``with:`` block 

1601 will not be subject to flushes occurring upon query 

1602 access. This is useful when initializing a series 

1603 of objects which involve existing database queries, 

1604 where the uncompleted object should not yet be flushed. 

1605 

1606 """ 

1607 autoflush = self.autoflush 

1608 self.autoflush = False 

1609 try: 

1610 yield self 

1611 finally: 

1612 self.autoflush = autoflush 

1613 

1614 def _autoflush(self): 

1615 if self.autoflush and not self._flushing: 

1616 try: 

1617 self.flush() 

1618 except sa_exc.StatementError as e: 

1619 # note we are reraising StatementError as opposed to 

1620 # raising FlushError with "chaining" to remain compatible 

1621 # with code that catches StatementError, IntegrityError, 

1622 # etc. 

1623 e.add_detail( 

1624 "raised as a result of Query-invoked autoflush; " 

1625 "consider using a session.no_autoflush block if this " 

1626 "flush is occurring prematurely" 

1627 ) 

1628 util.raise_(e, with_traceback=sys.exc_info()[2]) 

1629 

1630 def refresh( 

1631 self, 

1632 instance, 

1633 attribute_names=None, 

1634 with_for_update=None, 

1635 lockmode=None, 

1636 ): 

1637 """Expire and refresh the attributes on the given instance. 

1638 

1639 A query will be issued to the database and all attributes will be 

1640 refreshed with their current database value. 

1641 

1642 Lazy-loaded relational attributes will remain lazily loaded, so that 

1643 the instance-wide refresh operation will be followed immediately by 

1644 the lazy load of that attribute. 

1645 

1646 Eagerly-loaded relational attributes will eagerly load within the 

1647 single refresh operation. 

1648 

1649 Note that a highly isolated transaction will return the same values as 

1650 were previously read in that same transaction, regardless of changes 

1651 in database state outside of that transaction - usage of 

1652 :meth:`~Session.refresh` usually only makes sense if non-ORM SQL 

1653 statement were emitted in the ongoing transaction, or if autocommit 

1654 mode is turned on. 

1655 

1656 :param attribute_names: optional. An iterable collection of 

1657 string attribute names indicating a subset of attributes to 

1658 be refreshed. 

1659 

1660 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE 

1661 should be used, or may be a dictionary containing flags to 

1662 indicate a more specific set of FOR UPDATE flags for the SELECT; 

1663 flags should match the parameters of 

1664 :meth:`_query.Query.with_for_update`. 

1665 Supersedes the :paramref:`.Session.refresh.lockmode` parameter. 

1666 

1667 .. versionadded:: 1.2 

1668 

1669 :param lockmode: Passed to the :class:`~sqlalchemy.orm.query.Query` 

1670 as used by :meth:`~sqlalchemy.orm.query.Query.with_lockmode`. 

1671 Superseded by :paramref:`.Session.refresh.with_for_update`. 

1672 

1673 .. seealso:: 

1674 

1675 :ref:`session_expire` - introductory material 

1676 

1677 :meth:`.Session.expire` 

1678 

1679 :meth:`.Session.expire_all` 

1680 

1681 """ 

1682 try: 

1683 state = attributes.instance_state(instance) 

1684 except exc.NO_STATE as err: 

1685 util.raise_( 

1686 exc.UnmappedInstanceError(instance), replace_context=err, 

1687 ) 

1688 

1689 self._expire_state(state, attribute_names) 

1690 

1691 if with_for_update == {}: 

1692 raise sa_exc.ArgumentError( 

1693 "with_for_update should be the boolean value " 

1694 "True, or a dictionary with options. " 

1695 "A blank dictionary is ambiguous." 

1696 ) 

1697 

1698 if lockmode: 

1699 with_for_update = query.LockmodeArg.parse_legacy_query(lockmode) 

1700 elif with_for_update is not None: 

1701 if with_for_update is True: 

1702 with_for_update = query.LockmodeArg() 

1703 elif with_for_update: 

1704 with_for_update = query.LockmodeArg(**with_for_update) 

1705 else: 

1706 with_for_update = None 

1707 

1708 if ( 

1709 loading.load_on_ident( 

1710 self.query(object_mapper(instance)), 

1711 state.key, 

1712 refresh_state=state, 

1713 with_for_update=with_for_update, 

1714 only_load_props=attribute_names, 

1715 ) 

1716 is None 

1717 ): 

1718 raise sa_exc.InvalidRequestError( 

1719 "Could not refresh instance '%s'" % instance_str(instance) 

1720 ) 

1721 

1722 def expire_all(self): 

1723 """Expires all persistent instances within this Session. 

1724 

1725 When any attributes on a persistent instance is next accessed, 

1726 a query will be issued using the 

1727 :class:`.Session` object's current transactional context in order to 

1728 load all expired attributes for the given instance. Note that 

1729 a highly isolated transaction will return the same values as were 

1730 previously read in that same transaction, regardless of changes 

1731 in database state outside of that transaction. 

1732 

1733 To expire individual objects and individual attributes 

1734 on those objects, use :meth:`Session.expire`. 

1735 

1736 The :class:`.Session` object's default behavior is to 

1737 expire all state whenever the :meth:`Session.rollback` 

1738 or :meth:`Session.commit` methods are called, so that new 

1739 state can be loaded for the new transaction. For this reason, 

1740 calling :meth:`Session.expire_all` should not be needed when 

1741 autocommit is ``False``, assuming the transaction is isolated. 

1742 

1743 .. seealso:: 

1744 

1745 :ref:`session_expire` - introductory material 

1746 

1747 :meth:`.Session.expire` 

1748 

1749 :meth:`.Session.refresh` 

1750 

1751 """ 

1752 for state in self.identity_map.all_states(): 

1753 state._expire(state.dict, self.identity_map._modified) 

1754 

1755 def expire(self, instance, attribute_names=None): 

1756 """Expire the attributes on an instance. 

1757 

1758 Marks the attributes of an instance as out of date. When an expired 

1759 attribute is next accessed, a query will be issued to the 

1760 :class:`.Session` object's current transactional context in order to 

1761 load all expired attributes for the given instance. Note that 

1762 a highly isolated transaction will return the same values as were 

1763 previously read in that same transaction, regardless of changes 

1764 in database state outside of that transaction. 

1765 

1766 To expire all objects in the :class:`.Session` simultaneously, 

1767 use :meth:`Session.expire_all`. 

1768 

1769 The :class:`.Session` object's default behavior is to 

1770 expire all state whenever the :meth:`Session.rollback` 

1771 or :meth:`Session.commit` methods are called, so that new 

1772 state can be loaded for the new transaction. For this reason, 

1773 calling :meth:`Session.expire` only makes sense for the specific 

1774 case that a non-ORM SQL statement was emitted in the current 

1775 transaction. 

1776 

1777 :param instance: The instance to be refreshed. 

1778 :param attribute_names: optional list of string attribute names 

1779 indicating a subset of attributes to be expired. 

1780 

1781 .. seealso:: 

1782 

1783 :ref:`session_expire` - introductory material 

1784 

1785 :meth:`.Session.expire` 

1786 

1787 :meth:`.Session.refresh` 

1788 

1789 """ 

1790 try: 

1791 state = attributes.instance_state(instance) 

1792 except exc.NO_STATE as err: 

1793 util.raise_( 

1794 exc.UnmappedInstanceError(instance), replace_context=err, 

1795 ) 

1796 self._expire_state(state, attribute_names) 

1797 

1798 def _expire_state(self, state, attribute_names): 

1799 self._validate_persistent(state) 

1800 if attribute_names: 

1801 state._expire_attributes(state.dict, attribute_names) 

1802 else: 

1803 # pre-fetch the full cascade since the expire is going to 

1804 # remove associations 

1805 cascaded = list( 

1806 state.manager.mapper.cascade_iterator("refresh-expire", state) 

1807 ) 

1808 self._conditional_expire(state) 

1809 for o, m, st_, dct_ in cascaded: 

1810 self._conditional_expire(st_) 

1811 

1812 def _conditional_expire(self, state): 

1813 """Expire a state if persistent, else expunge if pending""" 

1814 

1815 if state.key: 

1816 state._expire(state.dict, self.identity_map._modified) 

1817 elif state in self._new: 

1818 self._new.pop(state) 

1819 state._detach(self) 

1820 

1821 @util.deprecated( 

1822 "0.7", 

1823 "The :meth:`.Session.prune` method is deprecated along with " 

1824 ":paramref:`.Session.weak_identity_map`. This method will be " 

1825 "removed in a future release.", 

1826 ) 

1827 def prune(self): 

1828 """Remove unreferenced instances cached in the identity map. 

1829 

1830 Note that this method is only meaningful if "weak_identity_map" is set 

1831 to False. The default weak identity map is self-pruning. 

1832 

1833 Removes any object in this Session's identity map that is not 

1834 referenced in user code, modified, new or scheduled for deletion. 

1835 Returns the number of objects pruned. 

1836 

1837 """ 

1838 return self.identity_map.prune() 

1839 

1840 def expunge(self, instance): 

1841 """Remove the `instance` from this ``Session``. 

1842 

1843 This will free all internal references to the instance. Cascading 

1844 will be applied according to the *expunge* cascade rule. 

1845 

1846 """ 

1847 try: 

1848 state = attributes.instance_state(instance) 

1849 except exc.NO_STATE as err: 

1850 util.raise_( 

1851 exc.UnmappedInstanceError(instance), replace_context=err, 

1852 ) 

1853 if state.session_id is not self.hash_key: 

1854 raise sa_exc.InvalidRequestError( 

1855 "Instance %s is not present in this Session" % state_str(state) 

1856 ) 

1857 

1858 cascaded = list( 

1859 state.manager.mapper.cascade_iterator("expunge", state) 

1860 ) 

1861 self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded]) 

1862 

1863 def _expunge_states(self, states, to_transient=False): 

1864 for state in states: 

1865 if state in self._new: 

1866 self._new.pop(state) 

1867 elif self.identity_map.contains_state(state): 

1868 self.identity_map.safe_discard(state) 

1869 self._deleted.pop(state, None) 

1870 elif self.transaction: 

1871 # state is "detached" from being deleted, but still present 

1872 # in the transaction snapshot 

1873 self.transaction._deleted.pop(state, None) 

1874 statelib.InstanceState._detach_states( 

1875 states, self, to_transient=to_transient 

1876 ) 

1877 

1878 def _register_persistent(self, states): 

1879 """Register all persistent objects from a flush. 

1880 

1881 This is used both for pending objects moving to the persistent 

1882 state as well as already persistent objects. 

1883 

1884 """ 

1885 

1886 pending_to_persistent = self.dispatch.pending_to_persistent or None 

1887 for state in states: 

1888 mapper = _state_mapper(state) 

1889 

1890 # prevent against last minute dereferences of the object 

1891 obj = state.obj() 

1892 if obj is not None: 

1893 

1894 instance_key = mapper._identity_key_from_state(state) 

1895 

1896 if ( 

1897 _none_set.intersection(instance_key[1]) 

1898 and not mapper.allow_partial_pks 

1899 or _none_set.issuperset(instance_key[1]) 

1900 ): 

1901 raise exc.FlushError( 

1902 "Instance %s has a NULL identity key. If this is an " 

1903 "auto-generated value, check that the database table " 

1904 "allows generation of new primary key values, and " 

1905 "that the mapped Column object is configured to " 

1906 "expect these generated values. Ensure also that " 

1907 "this flush() is not occurring at an inappropriate " 

1908 "time, such as within a load() event." 

1909 % state_str(state) 

1910 ) 

1911 

1912 if state.key is None: 

1913 state.key = instance_key 

1914 elif state.key != instance_key: 

1915 # primary key switch. use safe_discard() in case another 

1916 # state has already replaced this one in the identity 

1917 # map (see test/orm/test_naturalpks.py ReversePKsTest) 

1918 self.identity_map.safe_discard(state) 

1919 if state in self.transaction._key_switches: 

1920 orig_key = self.transaction._key_switches[state][0] 

1921 else: 

1922 orig_key = state.key 

1923 self.transaction._key_switches[state] = ( 

1924 orig_key, 

1925 instance_key, 

1926 ) 

1927 state.key = instance_key 

1928 

1929 # there can be an existing state in the identity map 

1930 # that is replaced when the primary keys of two instances 

1931 # are swapped; see test/orm/test_naturalpks.py -> test_reverse 

1932 old = self.identity_map.replace(state) 

1933 if ( 

1934 old is not None 

1935 and mapper._identity_key_from_state(old) == instance_key 

1936 and old.obj() is not None 

1937 ): 

1938 util.warn( 

1939 "Identity map already had an identity for %s, " 

1940 "replacing it with newly flushed object. Are there " 

1941 "load operations occurring inside of an event handler " 

1942 "within the flush?" % (instance_key,) 

1943 ) 

1944 state._orphaned_outside_of_session = False 

1945 

1946 statelib.InstanceState._commit_all_states( 

1947 ((state, state.dict) for state in states), self.identity_map 

1948 ) 

1949 

1950 self._register_altered(states) 

1951 

1952 if pending_to_persistent is not None: 

1953 for state in states.intersection(self._new): 

1954 pending_to_persistent(self, state) 

1955 

1956 # remove from new last, might be the last strong ref 

1957 for state in set(states).intersection(self._new): 

1958 self._new.pop(state) 

1959 

1960 def _register_altered(self, states): 

1961 if self._enable_transaction_accounting and self.transaction: 

1962 for state in states: 

1963 if state in self._new: 

1964 self.transaction._new[state] = True 

1965 else: 

1966 self.transaction._dirty[state] = True 

1967 

1968 def _remove_newly_deleted(self, states): 

1969 persistent_to_deleted = self.dispatch.persistent_to_deleted or None 

1970 for state in states: 

1971 if self._enable_transaction_accounting and self.transaction: 

1972 self.transaction._deleted[state] = True 

1973 

1974 if persistent_to_deleted is not None: 

1975 # get a strong reference before we pop out of 

1976 # self._deleted 

1977 obj = state.obj() # noqa 

1978 

1979 self.identity_map.safe_discard(state) 

1980 self._deleted.pop(state, None) 

1981 state._deleted = True 

1982 # can't call state._detach() here, because this state 

1983 # is still in the transaction snapshot and needs to be 

1984 # tracked as part of that 

1985 if persistent_to_deleted is not None: 

1986 persistent_to_deleted(self, state) 

1987 

1988 def add(self, instance, _warn=True): 

1989 """Place an object in the ``Session``. 

1990 

1991 Its state will be persisted to the database on the next flush 

1992 operation. 

1993 

1994 Repeated calls to ``add()`` will be ignored. The opposite of ``add()`` 

1995 is ``expunge()``. 

1996 

1997 """ 

1998 if _warn and self._warn_on_events: 

1999 self._flush_warning("Session.add()") 

2000 

2001 try: 

2002 state = attributes.instance_state(instance) 

2003 except exc.NO_STATE as err: 

2004 util.raise_( 

2005 exc.UnmappedInstanceError(instance), replace_context=err, 

2006 ) 

2007 

2008 self._save_or_update_state(state) 

2009 

2010 def add_all(self, instances): 

2011 """Add the given collection of instances to this ``Session``.""" 

2012 

2013 if self._warn_on_events: 

2014 self._flush_warning("Session.add_all()") 

2015 

2016 for instance in instances: 

2017 self.add(instance, _warn=False) 

2018 

2019 def _save_or_update_state(self, state): 

2020 state._orphaned_outside_of_session = False 

2021 self._save_or_update_impl(state) 

2022 

2023 mapper = _state_mapper(state) 

2024 for o, m, st_, dct_ in mapper.cascade_iterator( 

2025 "save-update", state, halt_on=self._contains_state 

2026 ): 

2027 self._save_or_update_impl(st_) 

2028 

2029 def delete(self, instance): 

2030 """Mark an instance as deleted. 

2031 

2032 The database delete operation occurs upon ``flush()``. 

2033 

2034 """ 

2035 if self._warn_on_events: 

2036 self._flush_warning("Session.delete()") 

2037 

2038 try: 

2039 state = attributes.instance_state(instance) 

2040 except exc.NO_STATE as err: 

2041 util.raise_( 

2042 exc.UnmappedInstanceError(instance), replace_context=err, 

2043 ) 

2044 

2045 self._delete_impl(state, instance, head=True) 

2046 

2047 def _delete_impl(self, state, obj, head): 

2048 

2049 if state.key is None: 

2050 if head: 

2051 raise sa_exc.InvalidRequestError( 

2052 "Instance '%s' is not persisted" % state_str(state) 

2053 ) 

2054 else: 

2055 return 

2056 

2057 to_attach = self._before_attach(state, obj) 

2058 

2059 if state in self._deleted: 

2060 return 

2061 

2062 self.identity_map.add(state) 

2063 

2064 if to_attach: 

2065 self._after_attach(state, obj) 

2066 

2067 if head: 

2068 # grab the cascades before adding the item to the deleted list 

2069 # so that autoflush does not delete the item 

2070 # the strong reference to the instance itself is significant here 

2071 cascade_states = list( 

2072 state.manager.mapper.cascade_iterator("delete", state) 

2073 ) 

2074 

2075 self._deleted[state] = obj 

2076 

2077 if head: 

2078 for o, m, st_, dct_ in cascade_states: 

2079 self._delete_impl(st_, o, False) 

2080 

2081 def merge(self, instance, load=True): 

2082 """Copy the state of a given instance into a corresponding instance 

2083 within this :class:`.Session`. 

2084 

2085 :meth:`.Session.merge` examines the primary key attributes of the 

2086 source instance, and attempts to reconcile it with an instance of the 

2087 same primary key in the session. If not found locally, it attempts 

2088 to load the object from the database based on primary key, and if 

2089 none can be located, creates a new instance. The state of each 

2090 attribute on the source instance is then copied to the target 

2091 instance. The resulting target instance is then returned by the 

2092 method; the original source instance is left unmodified, and 

2093 un-associated with the :class:`.Session` if not already. 

2094 

2095 This operation cascades to associated instances if the association is 

2096 mapped with ``cascade="merge"``. 

2097 

2098 See :ref:`unitofwork_merging` for a detailed discussion of merging. 

2099 

2100 .. versionchanged:: 1.1 - :meth:`.Session.merge` will now reconcile 

2101 pending objects with overlapping primary keys in the same way 

2102 as persistent. See :ref:`change_3601` for discussion. 

2103 

2104 :param instance: Instance to be merged. 

2105 :param load: Boolean, when False, :meth:`.merge` switches into 

2106 a "high performance" mode which causes it to forego emitting history 

2107 events as well as all database access. This flag is used for 

2108 cases such as transferring graphs of objects into a :class:`.Session` 

2109 from a second level cache, or to transfer just-loaded objects 

2110 into the :class:`.Session` owned by a worker thread or process 

2111 without re-querying the database. 

2112 

2113 The ``load=False`` use case adds the caveat that the given 

2114 object has to be in a "clean" state, that is, has no pending changes 

2115 to be flushed - even if the incoming object is detached from any 

2116 :class:`.Session`. This is so that when 

2117 the merge operation populates local attributes and 

2118 cascades to related objects and 

2119 collections, the values can be "stamped" onto the 

2120 target object as is, without generating any history or attribute 

2121 events, and without the need to reconcile the incoming data with 

2122 any existing related objects or collections that might not 

2123 be loaded. The resulting objects from ``load=False`` are always 

2124 produced as "clean", so it is only appropriate that the given objects 

2125 should be "clean" as well, else this suggests a mis-use of the 

2126 method. 

2127 

2128 

2129 .. seealso:: 

2130 

2131 :func:`.make_transient_to_detached` - provides for an alternative 

2132 means of "merging" a single object into the :class:`.Session` 

2133 

2134 """ 

2135 

2136 if self._warn_on_events: 

2137 self._flush_warning("Session.merge()") 

2138 

2139 _recursive = {} 

2140 _resolve_conflict_map = {} 

2141 

2142 if load: 

2143 # flush current contents if we expect to load data 

2144 self._autoflush() 

2145 

2146 object_mapper(instance) # verify mapped 

2147 autoflush = self.autoflush 

2148 try: 

2149 self.autoflush = False 

2150 return self._merge( 

2151 attributes.instance_state(instance), 

2152 attributes.instance_dict(instance), 

2153 load=load, 

2154 _recursive=_recursive, 

2155 _resolve_conflict_map=_resolve_conflict_map, 

2156 ) 

2157 finally: 

2158 self.autoflush = autoflush 

2159 

2160 def _merge( 

2161 self, 

2162 state, 

2163 state_dict, 

2164 load=True, 

2165 _recursive=None, 

2166 _resolve_conflict_map=None, 

2167 ): 

2168 mapper = _state_mapper(state) 

2169 if state in _recursive: 

2170 return _recursive[state] 

2171 

2172 new_instance = False 

2173 key = state.key 

2174 

2175 if key is None: 

2176 if state in self._new: 

2177 util.warn( 

2178 "Instance %s is already pending in this Session yet is " 

2179 "being merged again; this is probably not what you want " 

2180 "to do" % state_str(state) 

2181 ) 

2182 

2183 if not load: 

2184 raise sa_exc.InvalidRequestError( 

2185 "merge() with load=False option does not support " 

2186 "objects transient (i.e. unpersisted) objects. flush() " 

2187 "all changes on mapped instances before merging with " 

2188 "load=False." 

2189 ) 

2190 key = mapper._identity_key_from_state(state) 

2191 key_is_persistent = attributes.NEVER_SET not in key[1] and ( 

2192 not _none_set.intersection(key[1]) 

2193 or ( 

2194 mapper.allow_partial_pks 

2195 and not _none_set.issuperset(key[1]) 

2196 ) 

2197 ) 

2198 else: 

2199 key_is_persistent = True 

2200 

2201 if key in self.identity_map: 

2202 try: 

2203 merged = self.identity_map[key] 

2204 except KeyError: 

2205 # object was GC'ed right as we checked for it 

2206 merged = None 

2207 else: 

2208 merged = None 

2209 

2210 if merged is None: 

2211 if key_is_persistent and key in _resolve_conflict_map: 

2212 merged = _resolve_conflict_map[key] 

2213 

2214 elif not load: 

2215 if state.modified: 

2216 raise sa_exc.InvalidRequestError( 

2217 "merge() with load=False option does not support " 

2218 "objects marked as 'dirty'. flush() all changes on " 

2219 "mapped instances before merging with load=False." 

2220 ) 

2221 merged = mapper.class_manager.new_instance() 

2222 merged_state = attributes.instance_state(merged) 

2223 merged_state.key = key 

2224 self._update_impl(merged_state) 

2225 new_instance = True 

2226 

2227 elif key_is_persistent: 

2228 merged = self.query(mapper.class_).get(key[1]) 

2229 

2230 if merged is None: 

2231 merged = mapper.class_manager.new_instance() 

2232 merged_state = attributes.instance_state(merged) 

2233 merged_dict = attributes.instance_dict(merged) 

2234 new_instance = True 

2235 self._save_or_update_state(merged_state) 

2236 else: 

2237 merged_state = attributes.instance_state(merged) 

2238 merged_dict = attributes.instance_dict(merged) 

2239 

2240 _recursive[state] = merged 

2241 _resolve_conflict_map[key] = merged 

2242 

2243 # check that we didn't just pull the exact same 

2244 # state out. 

2245 if state is not merged_state: 

2246 # version check if applicable 

2247 if mapper.version_id_col is not None: 

2248 existing_version = mapper._get_state_attr_by_column( 

2249 state, 

2250 state_dict, 

2251 mapper.version_id_col, 

2252 passive=attributes.PASSIVE_NO_INITIALIZE, 

2253 ) 

2254 

2255 merged_version = mapper._get_state_attr_by_column( 

2256 merged_state, 

2257 merged_dict, 

2258 mapper.version_id_col, 

2259 passive=attributes.PASSIVE_NO_INITIALIZE, 

2260 ) 

2261 

2262 if ( 

2263 existing_version is not attributes.PASSIVE_NO_RESULT 

2264 and merged_version is not attributes.PASSIVE_NO_RESULT 

2265 and existing_version != merged_version 

2266 ): 

2267 raise exc.StaleDataError( 

2268 "Version id '%s' on merged state %s " 

2269 "does not match existing version '%s'. " 

2270 "Leave the version attribute unset when " 

2271 "merging to update the most recent version." 

2272 % ( 

2273 existing_version, 

2274 state_str(merged_state), 

2275 merged_version, 

2276 ) 

2277 ) 

2278 

2279 merged_state.load_path = state.load_path 

2280 merged_state.load_options = state.load_options 

2281 

2282 # since we are copying load_options, we need to copy 

2283 # the callables_ that would have been generated by those 

2284 # load_options. 

2285 # assumes that the callables we put in state.callables_ 

2286 # are not instance-specific (which they should not be) 

2287 merged_state._copy_callables(state) 

2288 

2289 for prop in mapper.iterate_properties: 

2290 prop.merge( 

2291 self, 

2292 state, 

2293 state_dict, 

2294 merged_state, 

2295 merged_dict, 

2296 load, 

2297 _recursive, 

2298 _resolve_conflict_map, 

2299 ) 

2300 

2301 if not load: 

2302 # remove any history 

2303 merged_state._commit_all(merged_dict, self.identity_map) 

2304 

2305 if new_instance: 

2306 merged_state.manager.dispatch.load(merged_state, None) 

2307 return merged 

2308 

2309 def _validate_persistent(self, state): 

2310 if not self.identity_map.contains_state(state): 

2311 raise sa_exc.InvalidRequestError( 

2312 "Instance '%s' is not persistent within this Session" 

2313 % state_str(state) 

2314 ) 

2315 

2316 def _save_impl(self, state): 

2317 if state.key is not None: 

2318 raise sa_exc.InvalidRequestError( 

2319 "Object '%s' already has an identity - " 

2320 "it can't be registered as pending" % state_str(state) 

2321 ) 

2322 

2323 obj = state.obj() 

2324 to_attach = self._before_attach(state, obj) 

2325 if state not in self._new: 

2326 self._new[state] = obj 

2327 state.insert_order = len(self._new) 

2328 if to_attach: 

2329 self._after_attach(state, obj) 

2330 

2331 def _update_impl(self, state, revert_deletion=False): 

2332 if state.key is None: 

2333 raise sa_exc.InvalidRequestError( 

2334 "Instance '%s' is not persisted" % state_str(state) 

2335 ) 

2336 

2337 if state._deleted: 

2338 if revert_deletion: 

2339 if not state._attached: 

2340 return 

2341 del state._deleted 

2342 else: 

2343 raise sa_exc.InvalidRequestError( 

2344 "Instance '%s' has been deleted. " 

2345 "Use the make_transient() " 

2346 "function to send this object back " 

2347 "to the transient state." % state_str(state) 

2348 ) 

2349 

2350 obj = state.obj() 

2351 

2352 # check for late gc 

2353 if obj is None: 

2354 return 

2355 

2356 to_attach = self._before_attach(state, obj) 

2357 

2358 self._deleted.pop(state, None) 

2359 if revert_deletion: 

2360 self.identity_map.replace(state) 

2361 else: 

2362 self.identity_map.add(state) 

2363 

2364 if to_attach: 

2365 self._after_attach(state, obj) 

2366 elif revert_deletion: 

2367 self.dispatch.deleted_to_persistent(self, state) 

2368 

2369 def _save_or_update_impl(self, state): 

2370 if state.key is None: 

2371 self._save_impl(state) 

2372 else: 

2373 self._update_impl(state) 

2374 

2375 def enable_relationship_loading(self, obj): 

2376 """Associate an object with this :class:`.Session` for related 

2377 object loading. 

2378 

2379 .. warning:: 

2380 

2381 :meth:`.enable_relationship_loading` exists to serve special 

2382 use cases and is not recommended for general use. 

2383 

2384 Accesses of attributes mapped with :func:`_orm.relationship` 

2385 will attempt to load a value from the database using this 

2386 :class:`.Session` as the source of connectivity. The values 

2387 will be loaded based on foreign key and primary key values 

2388 present on this object - if not present, then those relationships 

2389 will be unavailable. 

2390 

2391 The object will be attached to this session, but will 

2392 **not** participate in any persistence operations; its state 

2393 for almost all purposes will remain either "transient" or 

2394 "detached", except for the case of relationship loading. 

2395 

2396 Also note that backrefs will often not work as expected. 

2397 Altering a relationship-bound attribute on the target object 

2398 may not fire off a backref event, if the effective value 

2399 is what was already loaded from a foreign-key-holding value. 

2400 

2401 The :meth:`.Session.enable_relationship_loading` method is 

2402 similar to the ``load_on_pending`` flag on :func:`_orm.relationship`. 

2403 Unlike that flag, :meth:`.Session.enable_relationship_loading` allows 

2404 an object to remain transient while still being able to load 

2405 related items. 

2406 

2407 To make a transient object associated with a :class:`.Session` 

2408 via :meth:`.Session.enable_relationship_loading` pending, add 

2409 it to the :class:`.Session` using :meth:`.Session.add` normally. 

2410 If the object instead represents an existing identity in the database, 

2411 it should be merged using :meth:`.Session.merge`. 

2412 

2413 :meth:`.Session.enable_relationship_loading` does not improve 

2414 behavior when the ORM is used normally - object references should be 

2415 constructed at the object level, not at the foreign key level, so 

2416 that they are present in an ordinary way before flush() 

2417 proceeds. This method is not intended for general use. 

2418 

2419 .. seealso:: 

2420 

2421 ``load_on_pending`` at :func:`_orm.relationship` - this flag 

2422 allows per-relationship loading of many-to-ones on items that 

2423 are pending. 

2424 

2425 :func:`.make_transient_to_detached` - allows for an object to 

2426 be added to a :class:`.Session` without SQL emitted, which then 

2427 will unexpire attributes on access. 

2428 

2429 """ 

2430 state = attributes.instance_state(obj) 

2431 to_attach = self._before_attach(state, obj) 

2432 state._load_pending = True 

2433 if to_attach: 

2434 self._after_attach(state, obj) 

2435 

2436 def _before_attach(self, state, obj): 

2437 if state.session_id == self.hash_key: 

2438 return False 

2439 

2440 if state.session_id and state.session_id in _sessions: 

2441 raise sa_exc.InvalidRequestError( 

2442 "Object '%s' is already attached to session '%s' " 

2443 "(this is '%s')" 

2444 % (state_str(state), state.session_id, self.hash_key) 

2445 ) 

2446 

2447 self.dispatch.before_attach(self, state) 

2448 

2449 return True 

2450 

2451 def _after_attach(self, state, obj): 

2452 state.session_id = self.hash_key 

2453 if state.modified and state._strong_obj is None: 

2454 state._strong_obj = obj 

2455 self.dispatch.after_attach(self, state) 

2456 

2457 if state.key: 

2458 self.dispatch.detached_to_persistent(self, state) 

2459 else: 

2460 self.dispatch.transient_to_pending(self, state) 

2461 

2462 def __contains__(self, instance): 

2463 """Return True if the instance is associated with this session. 

2464 

2465 The instance may be pending or persistent within the Session for a 

2466 result of True. 

2467 

2468 """ 

2469 try: 

2470 state = attributes.instance_state(instance) 

2471 except exc.NO_STATE as err: 

2472 util.raise_( 

2473 exc.UnmappedInstanceError(instance), replace_context=err, 

2474 ) 

2475 return self._contains_state(state) 

2476 

2477 def __iter__(self): 

2478 """Iterate over all pending or persistent instances within this 

2479 Session. 

2480 

2481 """ 

2482 return iter( 

2483 list(self._new.values()) + list(self.identity_map.values()) 

2484 ) 

2485 

2486 def _contains_state(self, state): 

2487 return state in self._new or self.identity_map.contains_state(state) 

2488 

2489 def flush(self, objects=None): 

2490 """Flush all the object changes to the database. 

2491 

2492 Writes out all pending object creations, deletions and modifications 

2493 to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are 

2494 automatically ordered by the Session's unit of work dependency 

2495 solver. 

2496 

2497 Database operations will be issued in the current transactional 

2498 context and do not affect the state of the transaction, unless an 

2499 error occurs, in which case the entire transaction is rolled back. 

2500 You may flush() as often as you like within a transaction to move 

2501 changes from Python to the database's transaction buffer. 

2502 

2503 For ``autocommit`` Sessions with no active manual transaction, flush() 

2504 will create a transaction on the fly that surrounds the entire set of 

2505 operations into the flush. 

2506 

2507 :param objects: Optional; restricts the flush operation to operate 

2508 only on elements that are in the given collection. 

2509 

2510 This feature is for an extremely narrow set of use cases where 

2511 particular objects may need to be operated upon before the 

2512 full flush() occurs. It is not intended for general use. 

2513 

2514 """ 

2515 

2516 if self._flushing: 

2517 raise sa_exc.InvalidRequestError("Session is already flushing") 

2518 

2519 if self._is_clean(): 

2520 return 

2521 try: 

2522 self._flushing = True 

2523 self._flush(objects) 

2524 finally: 

2525 self._flushing = False 

2526 

2527 def _flush_warning(self, method): 

2528 util.warn( 

2529 "Usage of the '%s' operation is not currently supported " 

2530 "within the execution stage of the flush process. " 

2531 "Results may not be consistent. Consider using alternative " 

2532 "event listeners or connection-level operations instead." % method 

2533 ) 

2534 

2535 def _is_clean(self): 

2536 return ( 

2537 not self.identity_map.check_modified() 

2538 and not self._deleted 

2539 and not self._new 

2540 ) 

2541 

2542 def _flush(self, objects=None): 

2543 

2544 dirty = self._dirty_states 

2545 if not dirty and not self._deleted and not self._new: 

2546 self.identity_map._modified.clear() 

2547 return 

2548 

2549 flush_context = UOWTransaction(self) 

2550 

2551 if self.dispatch.before_flush: 

2552 self.dispatch.before_flush(self, flush_context, objects) 

2553 # re-establish "dirty states" in case the listeners 

2554 # added 

2555 dirty = self._dirty_states 

2556 

2557 deleted = set(self._deleted) 

2558 new = set(self._new) 

2559 

2560 dirty = set(dirty).difference(deleted) 

2561 

2562 # create the set of all objects we want to operate upon 

2563 if objects: 

2564 # specific list passed in 

2565 objset = set() 

2566 for o in objects: 

2567 try: 

2568 state = attributes.instance_state(o) 

2569 

2570 except exc.NO_STATE as err: 

2571 util.raise_( 

2572 exc.UnmappedInstanceError(o), replace_context=err, 

2573 ) 

2574 objset.add(state) 

2575 else: 

2576 objset = None 

2577 

2578 # store objects whose fate has been decided 

2579 processed = set() 

2580 

2581 # put all saves/updates into the flush context. detect top-level 

2582 # orphans and throw them into deleted. 

2583 if objset: 

2584 proc = new.union(dirty).intersection(objset).difference(deleted) 

2585 else: 

2586 proc = new.union(dirty).difference(deleted) 

2587 

2588 for state in proc: 

2589 is_orphan = _state_mapper(state)._is_orphan(state) 

2590 

2591 is_persistent_orphan = is_orphan and state.has_identity 

2592 

2593 if ( 

2594 is_orphan 

2595 and not is_persistent_orphan 

2596 and state._orphaned_outside_of_session 

2597 ): 

2598 self._expunge_states([state]) 

2599 else: 

2600 _reg = flush_context.register_object( 

2601 state, isdelete=is_persistent_orphan 

2602 ) 

2603 assert _reg, "Failed to add object to the flush context!" 

2604 processed.add(state) 

2605 

2606 # put all remaining deletes into the flush context. 

2607 if objset: 

2608 proc = deleted.intersection(objset).difference(processed) 

2609 else: 

2610 proc = deleted.difference(processed) 

2611 for state in proc: 

2612 _reg = flush_context.register_object(state, isdelete=True) 

2613 assert _reg, "Failed to add object to the flush context!" 

2614 

2615 if not flush_context.has_work: 

2616 return 

2617 

2618 flush_context.transaction = transaction = self.begin( 

2619 subtransactions=True 

2620 ) 

2621 try: 

2622 self._warn_on_events = True 

2623 try: 

2624 flush_context.execute() 

2625 finally: 

2626 self._warn_on_events = False 

2627 

2628 self.dispatch.after_flush(self, flush_context) 

2629 

2630 flush_context.finalize_flush_changes() 

2631 

2632 if not objects and self.identity_map._modified: 

2633 len_ = len(self.identity_map._modified) 

2634 

2635 statelib.InstanceState._commit_all_states( 

2636 [ 

2637 (state, state.dict) 

2638 for state in self.identity_map._modified 

2639 ], 

2640 instance_dict=self.identity_map, 

2641 ) 

2642 util.warn( 

2643 "Attribute history events accumulated on %d " 

2644 "previously clean instances " 

2645 "within inner-flush event handlers have been " 

2646 "reset, and will not result in database updates. " 

2647 "Consider using set_committed_value() within " 

2648 "inner-flush event handlers to avoid this warning." % len_ 

2649 ) 

2650 

2651 # useful assertions: 

2652 # if not objects: 

2653 # assert not self.identity_map._modified 

2654 # else: 

2655 # assert self.identity_map._modified == \ 

2656 # self.identity_map._modified.difference(objects) 

2657 

2658 self.dispatch.after_flush_postexec(self, flush_context) 

2659 

2660 transaction.commit() 

2661 

2662 except: 

2663 with util.safe_reraise(): 

2664 transaction.rollback(_capture_exception=True) 

2665 

2666 def bulk_save_objects( 

2667 self, 

2668 objects, 

2669 return_defaults=False, 

2670 update_changed_only=True, 

2671 preserve_order=True, 

2672 ): 

2673 """Perform a bulk save of the given list of objects. 

2674 

2675 The bulk save feature allows mapped objects to be used as the 

2676 source of simple INSERT and UPDATE operations which can be more easily 

2677 grouped together into higher performing "executemany" 

2678 operations; the extraction of data from the objects is also performed 

2679 using a lower-latency process that ignores whether or not attributes 

2680 have actually been modified in the case of UPDATEs, and also ignores 

2681 SQL expressions. 

2682 

2683 The objects as given are not added to the session and no additional 

2684 state is established on them, unless the ``return_defaults`` flag 

2685 is also set, in which case primary key attributes and server-side 

2686 default values will be populated. 

2687 

2688 .. versionadded:: 1.0.0 

2689 

2690 .. warning:: 

2691 

2692 The bulk save feature allows for a lower-latency INSERT/UPDATE 

2693 of rows at the expense of most other unit-of-work features. 

2694 Features such as object management, relationship handling, 

2695 and SQL clause support are **silently omitted** in favor of raw 

2696 INSERT/UPDATES of records. 

2697 

2698 **Please read the list of caveats at** :ref:`bulk_operations` 

2699 **before using this method, and fully test and confirm the 

2700 functionality of all code developed using these systems.** 

2701 

2702 :param objects: a sequence of mapped object instances. The mapped 

2703 objects are persisted as is, and are **not** associated with the 

2704 :class:`.Session` afterwards. 

2705 

2706 For each object, whether the object is sent as an INSERT or an 

2707 UPDATE is dependent on the same rules used by the :class:`.Session` 

2708 in traditional operation; if the object has the 

2709 :attr:`.InstanceState.key` 

2710 attribute set, then the object is assumed to be "detached" and 

2711 will result in an UPDATE. Otherwise, an INSERT is used. 

2712 

2713 In the case of an UPDATE, statements are grouped based on which 

2714 attributes have changed, and are thus to be the subject of each 

2715 SET clause. If ``update_changed_only`` is False, then all 

2716 attributes present within each object are applied to the UPDATE 

2717 statement, which may help in allowing the statements to be grouped 

2718 together into a larger executemany(), and will also reduce the 

2719 overhead of checking history on attributes. 

2720 

2721 :param return_defaults: when True, rows that are missing values which 

2722 generate defaults, namely integer primary key defaults and sequences, 

2723 will be inserted **one at a time**, so that the primary key value 

2724 is available. In particular this will allow joined-inheritance 

2725 and other multi-table mappings to insert correctly without the need 

2726 to provide primary key values ahead of time; however, 

2727 :paramref:`.Session.bulk_save_objects.return_defaults` **greatly 

2728 reduces the performance gains** of the method overall. 

2729 

2730 :param update_changed_only: when True, UPDATE statements are rendered 

2731 based on those attributes in each state that have logged changes. 

2732 When False, all attributes present are rendered into the SET clause 

2733 with the exception of primary key attributes. 

2734 

2735 :param preserve_order: when True, the order of inserts and updates 

2736 matches exactly the order in which the objects are given. When 

2737 False, common types of objects are grouped into inserts 

2738 and updates, to allow for more batching opportunities. 

2739 

2740 .. versionadded:: 1.3 

2741 

2742 .. seealso:: 

2743 

2744 :ref:`bulk_operations` 

2745 

2746 :meth:`.Session.bulk_insert_mappings` 

2747 

2748 :meth:`.Session.bulk_update_mappings` 

2749 

2750 """ 

2751 

2752 def key(state): 

2753 return (state.mapper, state.key is not None) 

2754 

2755 obj_states = (attributes.instance_state(obj) for obj in objects) 

2756 if not preserve_order: 

2757 obj_states = sorted(obj_states, key=key) 

2758 

2759 for (mapper, isupdate), states in itertools.groupby(obj_states, key): 

2760 self._bulk_save_mappings( 

2761 mapper, 

2762 states, 

2763 isupdate, 

2764 True, 

2765 return_defaults, 

2766 update_changed_only, 

2767 False, 

2768 ) 

2769 

2770 def bulk_insert_mappings( 

2771 self, mapper, mappings, return_defaults=False, render_nulls=False 

2772 ): 

2773 """Perform a bulk insert of the given list of mapping dictionaries. 

2774 

2775 The bulk insert feature allows plain Python dictionaries to be used as 

2776 the source of simple INSERT operations which can be more easily 

2777 grouped together into higher performing "executemany" 

2778 operations. Using dictionaries, there is no "history" or session 

2779 state management features in use, reducing latency when inserting 

2780 large numbers of simple rows. 

2781 

2782 The values within the dictionaries as given are typically passed 

2783 without modification into Core :meth:`_expression.Insert` constructs, 

2784 after 

2785 organizing the values within them across the tables to which 

2786 the given mapper is mapped. 

2787 

2788 .. versionadded:: 1.0.0 

2789 

2790 .. warning:: 

2791 

2792 The bulk insert feature allows for a lower-latency INSERT 

2793 of rows at the expense of most other unit-of-work features. 

2794 Features such as object management, relationship handling, 

2795 and SQL clause support are **silently omitted** in favor of raw 

2796 INSERT of records. 

2797 

2798 **Please read the list of caveats at** :ref:`bulk_operations` 

2799 **before using this method, and fully test and confirm the 

2800 functionality of all code developed using these systems.** 

2801 

2802 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 

2803 object, 

2804 representing the single kind of object represented within the mapping 

2805 list. 

2806 

2807 :param mappings: a sequence of dictionaries, each one containing the 

2808 state of the mapped row to be inserted, in terms of the attribute 

2809 names on the mapped class. If the mapping refers to multiple tables, 

2810 such as a joined-inheritance mapping, each dictionary must contain all 

2811 keys to be populated into all tables. 

2812 

2813 :param return_defaults: when True, rows that are missing values which 

2814 generate defaults, namely integer primary key defaults and sequences, 

2815 will be inserted **one at a time**, so that the primary key value 

2816 is available. In particular this will allow joined-inheritance 

2817 and other multi-table mappings to insert correctly without the need 

2818 to provide primary 

2819 key values ahead of time; however, 

2820 :paramref:`.Session.bulk_insert_mappings.return_defaults` 

2821 **greatly reduces the performance gains** of the method overall. 

2822 If the rows 

2823 to be inserted only refer to a single table, then there is no 

2824 reason this flag should be set as the returned default information 

2825 is not used. 

2826 

2827 :param render_nulls: When True, a value of ``None`` will result 

2828 in a NULL value being included in the INSERT statement, rather 

2829 than the column being omitted from the INSERT. This allows all 

2830 the rows being INSERTed to have the identical set of columns which 

2831 allows the full set of rows to be batched to the DBAPI. Normally, 

2832 each column-set that contains a different combination of NULL values 

2833 than the previous row must omit a different series of columns from 

2834 the rendered INSERT statement, which means it must be emitted as a 

2835 separate statement. By passing this flag, the full set of rows 

2836 are guaranteed to be batchable into one batch; the cost however is 

2837 that server-side defaults which are invoked by an omitted column will 

2838 be skipped, so care must be taken to ensure that these are not 

2839 necessary. 

2840 

2841 .. warning:: 

2842 

2843 When this flag is set, **server side default SQL values will 

2844 not be invoked** for those columns that are inserted as NULL; 

2845 the NULL value will be sent explicitly. Care must be taken 

2846 to ensure that no server-side default functions need to be 

2847 invoked for the operation as a whole. 

2848 

2849 .. versionadded:: 1.1 

2850 

2851 .. seealso:: 

2852 

2853 :ref:`bulk_operations` 

2854 

2855 :meth:`.Session.bulk_save_objects` 

2856 

2857 :meth:`.Session.bulk_update_mappings` 

2858 

2859 """ 

2860 self._bulk_save_mappings( 

2861 mapper, 

2862 mappings, 

2863 False, 

2864 False, 

2865 return_defaults, 

2866 False, 

2867 render_nulls, 

2868 ) 

2869 

2870 def bulk_update_mappings(self, mapper, mappings): 

2871 """Perform a bulk update of the given list of mapping dictionaries. 

2872 

2873 The bulk update feature allows plain Python dictionaries to be used as 

2874 the source of simple UPDATE operations which can be more easily 

2875 grouped together into higher performing "executemany" 

2876 operations. Using dictionaries, there is no "history" or session 

2877 state management features in use, reducing latency when updating 

2878 large numbers of simple rows. 

2879 

2880 .. versionadded:: 1.0.0 

2881 

2882 .. warning:: 

2883 

2884 The bulk update feature allows for a lower-latency UPDATE 

2885 of rows at the expense of most other unit-of-work features. 

2886 Features such as object management, relationship handling, 

2887 and SQL clause support are **silently omitted** in favor of raw 

2888 UPDATES of records. 

2889 

2890 **Please read the list of caveats at** :ref:`bulk_operations` 

2891 **before using this method, and fully test and confirm the 

2892 functionality of all code developed using these systems.** 

2893 

2894 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 

2895 object, 

2896 representing the single kind of object represented within the mapping 

2897 list. 

2898 

2899 :param mappings: a sequence of dictionaries, each one containing the 

2900 state of the mapped row to be updated, in terms of the attribute names 

2901 on the mapped class. If the mapping refers to multiple tables, such 

2902 as a joined-inheritance mapping, each dictionary may contain keys 

2903 corresponding to all tables. All those keys which are present and 

2904 are not part of the primary key are applied to the SET clause of the 

2905 UPDATE statement; the primary key values, which are required, are 

2906 applied to the WHERE clause. 

2907 

2908 

2909 .. seealso:: 

2910 

2911 :ref:`bulk_operations` 

2912 

2913 :meth:`.Session.bulk_insert_mappings` 

2914 

2915 :meth:`.Session.bulk_save_objects` 

2916 

2917 """ 

2918 self._bulk_save_mappings( 

2919 mapper, mappings, True, False, False, False, False 

2920 ) 

2921 

2922 def _bulk_save_mappings( 

2923 self, 

2924 mapper, 

2925 mappings, 

2926 isupdate, 

2927 isstates, 

2928 return_defaults, 

2929 update_changed_only, 

2930 render_nulls, 

2931 ): 

2932 mapper = _class_to_mapper(mapper) 

2933 self._flushing = True 

2934 

2935 transaction = self.begin(subtransactions=True) 

2936 try: 

2937 if isupdate: 

2938 persistence._bulk_update( 

2939 mapper, 

2940 mappings, 

2941 transaction, 

2942 isstates, 

2943 update_changed_only, 

2944 ) 

2945 else: 

2946 persistence._bulk_insert( 

2947 mapper, 

2948 mappings, 

2949 transaction, 

2950 isstates, 

2951 return_defaults, 

2952 render_nulls, 

2953 ) 

2954 transaction.commit() 

2955 

2956 except: 

2957 with util.safe_reraise(): 

2958 transaction.rollback(_capture_exception=True) 

2959 finally: 

2960 self._flushing = False 

2961 

2962 @util.deprecated_params( 

2963 passive=( 

2964 "0.8", 

2965 "The :paramref:`.Session.is_modified.passive` flag is deprecated " 

2966 "and will be removed in a future release. The flag is no longer " 

2967 "used and is ignored.", 

2968 ) 

2969 ) 

2970 def is_modified(self, instance, include_collections=True, passive=None): 

2971 r"""Return ``True`` if the given instance has locally 

2972 modified attributes. 

2973 

2974 This method retrieves the history for each instrumented 

2975 attribute on the instance and performs a comparison of the current 

2976 value to its previously committed value, if any. 

2977 

2978 It is in effect a more expensive and accurate 

2979 version of checking for the given instance in the 

2980 :attr:`.Session.dirty` collection; a full test for 

2981 each attribute's net "dirty" status is performed. 

2982 

2983 E.g.:: 

2984 

2985 return session.is_modified(someobject) 

2986 

2987 A few caveats to this method apply: 

2988 

2989 * Instances present in the :attr:`.Session.dirty` collection may 

2990 report ``False`` when tested with this method. This is because 

2991 the object may have received change events via attribute mutation, 

2992 thus placing it in :attr:`.Session.dirty`, but ultimately the state 

2993 is the same as that loaded from the database, resulting in no net 

2994 change here. 

2995 * Scalar attributes may not have recorded the previously set 

2996 value when a new value was applied, if the attribute was not loaded, 

2997 or was expired, at the time the new value was received - in these 

2998 cases, the attribute is assumed to have a change, even if there is 

2999 ultimately no net change against its database value. SQLAlchemy in 

3000 most cases does not need the "old" value when a set event occurs, so 

3001 it skips the expense of a SQL call if the old value isn't present, 

3002 based on the assumption that an UPDATE of the scalar value is 

3003 usually needed, and in those few cases where it isn't, is less 

3004 expensive on average than issuing a defensive SELECT. 

3005 

3006 The "old" value is fetched unconditionally upon set only if the 

3007 attribute container has the ``active_history`` flag set to ``True``. 

3008 This flag is set typically for primary key attributes and scalar 

3009 object references that are not a simple many-to-one. To set this 

3010 flag for any arbitrary mapped column, use the ``active_history`` 

3011 argument with :func:`.column_property`. 

3012 

3013 :param instance: mapped instance to be tested for pending changes. 

3014 :param include_collections: Indicates if multivalued collections 

3015 should be included in the operation. Setting this to ``False`` is a 

3016 way to detect only local-column based properties (i.e. scalar columns 

3017 or many-to-one foreign keys) that would result in an UPDATE for this 

3018 instance upon flush. 

3019 :param passive: not used 

3020 

3021 """ 

3022 state = object_state(instance) 

3023 

3024 if not state.modified: 

3025 return False 

3026 

3027 dict_ = state.dict 

3028 

3029 for attr in state.manager.attributes: 

3030 if ( 

3031 not include_collections 

3032 and hasattr(attr.impl, "get_collection") 

3033 ) or not hasattr(attr.impl, "get_history"): 

3034 continue 

3035 

3036 (added, unchanged, deleted) = attr.impl.get_history( 

3037 state, dict_, passive=attributes.NO_CHANGE 

3038 ) 

3039 

3040 if added or deleted: 

3041 return True 

3042 else: 

3043 return False 

3044 

3045 @property 

3046 def is_active(self): 

3047 """True if this :class:`.Session` is in "transaction mode" and 

3048 is not in "partial rollback" state. 

3049 

3050 The :class:`.Session` in its default mode of ``autocommit=False`` 

3051 is essentially always in "transaction mode", in that a 

3052 :class:`.SessionTransaction` is associated with it as soon as 

3053 it is instantiated. This :class:`.SessionTransaction` is immediately 

3054 replaced with a new one as soon as it is ended, due to a rollback, 

3055 commit, or close operation. 

3056 

3057 "Transaction mode" does *not* indicate whether 

3058 or not actual database connection resources are in use; the 

3059 :class:`.SessionTransaction` object coordinates among zero or more 

3060 actual database transactions, and starts out with none, accumulating 

3061 individual DBAPI connections as different data sources are used 

3062 within its scope. The best way to track when a particular 

3063 :class:`.Session` has actually begun to use DBAPI resources is to 

3064 implement a listener using the :meth:`.SessionEvents.after_begin` 

3065 method, which will deliver both the :class:`.Session` as well as the 

3066 target :class:`_engine.Connection` to a user-defined event listener. 

3067 

3068 The "partial rollback" state refers to when an "inner" transaction, 

3069 typically used during a flush, encounters an error and emits a 

3070 rollback of the DBAPI connection. At this point, the 

3071 :class:`.Session` is in "partial rollback" and awaits for the user to 

3072 call :meth:`.Session.rollback`, in order to close out the 

3073 transaction stack. It is in this "partial rollback" period that the 

3074 :attr:`.is_active` flag returns False. After the call to 

3075 :meth:`.Session.rollback`, the :class:`.SessionTransaction` is 

3076 replaced with a new one and :attr:`.is_active` returns ``True`` again. 

3077 

3078 When a :class:`.Session` is used in ``autocommit=True`` mode, the 

3079 :class:`.SessionTransaction` is only instantiated within the scope 

3080 of a flush call, or when :meth:`.Session.begin` is called. So 

3081 :attr:`.is_active` will always be ``False`` outside of a flush or 

3082 :meth:`.Session.begin` block in this mode, and will be ``True`` 

3083 within the :meth:`.Session.begin` block as long as it doesn't enter 

3084 "partial rollback" state. 

3085 

3086 From all the above, it follows that the only purpose to this flag is 

3087 for application frameworks that wish to detect if a "rollback" is 

3088 necessary within a generic error handling routine, for 

3089 :class:`.Session` objects that would otherwise be in 

3090 "partial rollback" mode. In a typical integration case, this is also 

3091 not necessary as it is standard practice to emit 

3092 :meth:`.Session.rollback` unconditionally within the outermost 

3093 exception catch. 

3094 

3095 To track the transactional state of a :class:`.Session` fully, 

3096 use event listeners, primarily the :meth:`.SessionEvents.after_begin`, 

3097 :meth:`.SessionEvents.after_commit`, 

3098 :meth:`.SessionEvents.after_rollback` and related events. 

3099 

3100 """ 

3101 return self.transaction and self.transaction.is_active 

3102 

3103 identity_map = None 

3104 """A mapping of object identities to objects themselves. 

3105 

3106 Iterating through ``Session.identity_map.values()`` provides 

3107 access to the full set of persistent objects (i.e., those 

3108 that have row identity) currently in the session. 

3109 

3110 .. seealso:: 

3111 

3112 :func:`.identity_key` - helper function to produce the keys used 

3113 in this dictionary. 

3114 

3115 """ 

3116 

3117 @property 

3118 def _dirty_states(self): 

3119 """The set of all persistent states considered dirty. 

3120 

3121 This method returns all states that were modified including 

3122 those that were possibly deleted. 

3123 

3124 """ 

3125 return self.identity_map._dirty_states() 

3126 

3127 @property 

3128 def dirty(self): 

3129 """The set of all persistent instances considered dirty. 

3130 

3131 E.g.:: 

3132 

3133 some_mapped_object in session.dirty 

3134 

3135 Instances are considered dirty when they were modified but not 

3136 deleted. 

3137 

3138 Note that this 'dirty' calculation is 'optimistic'; most 

3139 attribute-setting or collection modification operations will 

3140 mark an instance as 'dirty' and place it in this set, even if 

3141 there is no net change to the attribute's value. At flush 

3142 time, the value of each attribute is compared to its 

3143 previously saved value, and if there's no net change, no SQL 

3144 operation will occur (this is a more expensive operation so 

3145 it's only done at flush time). 

3146 

3147 To check if an instance has actionable net changes to its 

3148 attributes, use the :meth:`.Session.is_modified` method. 

3149 

3150 """ 

3151 return util.IdentitySet( 

3152 [ 

3153 state.obj() 

3154 for state in self._dirty_states 

3155 if state not in self._deleted 

3156 ] 

3157 ) 

3158 

3159 @property 

3160 def deleted(self): 

3161 "The set of all instances marked as 'deleted' within this ``Session``" 

3162 

3163 return util.IdentitySet(list(self._deleted.values())) 

3164 

3165 @property 

3166 def new(self): 

3167 "The set of all instances marked as 'new' within this ``Session``." 

3168 

3169 return util.IdentitySet(list(self._new.values())) 

3170 

3171 

3172class sessionmaker(_SessionClassMethods): 

3173 """A configurable :class:`.Session` factory. 

3174 

3175 The :class:`.sessionmaker` factory generates new 

3176 :class:`.Session` objects when called, creating them given 

3177 the configurational arguments established here. 

3178 

3179 e.g.:: 

3180 

3181 # global scope 

3182 Session = sessionmaker(autoflush=False) 

3183 

3184 # later, in a local scope, create and use a session: 

3185 sess = Session() 

3186 

3187 Any keyword arguments sent to the constructor itself will override the 

3188 "configured" keywords:: 

3189 

3190 Session = sessionmaker() 

3191 

3192 # bind an individual session to a connection 

3193 sess = Session(bind=connection) 

3194 

3195 The class also includes a method :meth:`.configure`, which can 

3196 be used to specify additional keyword arguments to the factory, which 

3197 will take effect for subsequent :class:`.Session` objects generated. 

3198 This is usually used to associate one or more :class:`_engine.Engine` 

3199 objects 

3200 with an existing :class:`.sessionmaker` factory before it is first 

3201 used:: 

3202 

3203 # application starts 

3204 Session = sessionmaker() 

3205 

3206 # ... later 

3207 engine = create_engine('sqlite:///foo.db') 

3208 Session.configure(bind=engine) 

3209 

3210 sess = Session() 

3211 

3212 .. seealso: 

3213 

3214 :ref:`session_getting` - introductory text on creating 

3215 sessions using :class:`.sessionmaker`. 

3216 

3217 """ 

3218 

3219 def __init__( 

3220 self, 

3221 bind=None, 

3222 class_=Session, 

3223 autoflush=True, 

3224 autocommit=False, 

3225 expire_on_commit=True, 

3226 info=None, 

3227 **kw 

3228 ): 

3229 r"""Construct a new :class:`.sessionmaker`. 

3230 

3231 All arguments here except for ``class_`` correspond to arguments 

3232 accepted by :class:`.Session` directly. See the 

3233 :meth:`.Session.__init__` docstring for more details on parameters. 

3234 

3235 :param bind: a :class:`_engine.Engine` or other :class:`.Connectable` 

3236 with 

3237 which newly created :class:`.Session` objects will be associated. 

3238 :param class\_: class to use in order to create new :class:`.Session` 

3239 objects. Defaults to :class:`.Session`. 

3240 :param autoflush: The autoflush setting to use with newly created 

3241 :class:`.Session` objects. 

3242 :param autocommit: The autocommit setting to use with newly created 

3243 :class:`.Session` objects. 

3244 :param expire_on_commit=True: the expire_on_commit setting to use 

3245 with newly created :class:`.Session` objects. 

3246 :param info: optional dictionary of information that will be available 

3247 via :attr:`.Session.info`. Note this dictionary is *updated*, not 

3248 replaced, when the ``info`` parameter is specified to the specific 

3249 :class:`.Session` construction operation. 

3250 

3251 .. versionadded:: 0.9.0 

3252 

3253 :param \**kw: all other keyword arguments are passed to the 

3254 constructor of newly created :class:`.Session` objects. 

3255 

3256 """ 

3257 kw["bind"] = bind 

3258 kw["autoflush"] = autoflush 

3259 kw["autocommit"] = autocommit 

3260 kw["expire_on_commit"] = expire_on_commit 

3261 if info is not None: 

3262 kw["info"] = info 

3263 self.kw = kw 

3264 # make our own subclass of the given class, so that 

3265 # events can be associated with it specifically. 

3266 self.class_ = type(class_.__name__, (class_,), {}) 

3267 

3268 def __call__(self, **local_kw): 

3269 """Produce a new :class:`.Session` object using the configuration 

3270 established in this :class:`.sessionmaker`. 

3271 

3272 In Python, the ``__call__`` method is invoked on an object when 

3273 it is "called" in the same way as a function:: 

3274 

3275 Session = sessionmaker() 

3276 session = Session() # invokes sessionmaker.__call__() 

3277 

3278 """ 

3279 for k, v in self.kw.items(): 

3280 if k == "info" and "info" in local_kw: 

3281 d = v.copy() 

3282 d.update(local_kw["info"]) 

3283 local_kw["info"] = d 

3284 else: 

3285 local_kw.setdefault(k, v) 

3286 return self.class_(**local_kw) 

3287 

3288 def configure(self, **new_kw): 

3289 """(Re)configure the arguments for this sessionmaker. 

3290 

3291 e.g.:: 

3292 

3293 Session = sessionmaker() 

3294 

3295 Session.configure(bind=create_engine('sqlite://')) 

3296 """ 

3297 self.kw.update(new_kw) 

3298 

3299 def __repr__(self): 

3300 return "%s(class_=%r, %s)" % ( 

3301 self.__class__.__name__, 

3302 self.class_.__name__, 

3303 ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), 

3304 ) 

3305 

3306 

3307def close_all_sessions(): 

3308 """Close all sessions in memory. 

3309 

3310 This function consults a global registry of all :class:`.Session` objects 

3311 and calls :meth:`.Session.close` on them, which resets them to a clean 

3312 state. 

3313 

3314 This function is not for general use but may be useful for test suites 

3315 within the teardown scheme. 

3316 

3317 .. versionadded:: 1.3 

3318 

3319 """ 

3320 

3321 for sess in _sessions.values(): 

3322 sess.close() 

3323 

3324 

3325def make_transient(instance): 

3326 """Alter the state of the given instance so that it is :term:`transient`. 

3327 

3328 .. note:: 

3329 

3330 :func:`.make_transient` is a special-case function for 

3331 advanced use cases only. 

3332 

3333 The given mapped instance is assumed to be in the :term:`persistent` or 

3334 :term:`detached` state. The function will remove its association with any 

3335 :class:`.Session` as well as its :attr:`.InstanceState.identity`. The 

3336 effect is that the object will behave as though it were newly constructed, 

3337 except retaining any attribute / collection values that were loaded at the 

3338 time of the call. The :attr:`.InstanceState.deleted` flag is also reset 

3339 if this object had been deleted as a result of using 

3340 :meth:`.Session.delete`. 

3341 

3342 .. warning:: 

3343 

3344 :func:`.make_transient` does **not** "unexpire" or otherwise eagerly 

3345 load ORM-mapped attributes that are not currently loaded at the time 

3346 the function is called. This includes attributes which: 

3347 

3348 * were expired via :meth:`.Session.expire` 

3349 

3350 * were expired as the natural effect of committing a session 

3351 transaction, e.g. :meth:`.Session.commit` 

3352 

3353 * are normally :term:`lazy loaded` but are not currently loaded 

3354 

3355 * are "deferred" via :ref:`deferred` and are not yet loaded 

3356 

3357 * were not present in the query which loaded this object, such as that 

3358 which is common in joined table inheritance and other scenarios. 

3359 

3360 After :func:`.make_transient` is called, unloaded attributes such 

3361 as those above will normally resolve to the value ``None`` when 

3362 accessed, or an empty collection for a collection-oriented attribute. 

3363 As the object is transient and un-associated with any database 

3364 identity, it will no longer retrieve these values. 

3365 

3366 .. seealso:: 

3367 

3368 :func:`.make_transient_to_detached` 

3369 

3370 """ 

3371 state = attributes.instance_state(instance) 

3372 s = _state_session(state) 

3373 if s: 

3374 s._expunge_states([state]) 

3375 

3376 # remove expired state 

3377 state.expired_attributes.clear() 

3378 

3379 # remove deferred callables 

3380 if state.callables: 

3381 del state.callables 

3382 

3383 if state.key: 

3384 del state.key 

3385 if state._deleted: 

3386 del state._deleted 

3387 

3388 

3389def make_transient_to_detached(instance): 

3390 """Make the given transient instance :term:`detached`. 

3391 

3392 .. note:: 

3393 

3394 :func:`.make_transient_to_detached` is a special-case function for 

3395 advanced use cases only. 

3396 

3397 All attribute history on the given instance 

3398 will be reset as though the instance were freshly loaded 

3399 from a query. Missing attributes will be marked as expired. 

3400 The primary key attributes of the object, which are required, will be made 

3401 into the "key" of the instance. 

3402 

3403 The object can then be added to a session, or merged 

3404 possibly with the load=False flag, at which point it will look 

3405 as if it were loaded that way, without emitting SQL. 

3406 

3407 This is a special use case function that differs from a normal 

3408 call to :meth:`.Session.merge` in that a given persistent state 

3409 can be manufactured without any SQL calls. 

3410 

3411 .. versionadded:: 0.9.5 

3412 

3413 .. seealso:: 

3414 

3415 :func:`.make_transient` 

3416 

3417 :meth:`.Session.enable_relationship_loading` 

3418 

3419 """ 

3420 state = attributes.instance_state(instance) 

3421 if state.session_id or state.key: 

3422 raise sa_exc.InvalidRequestError("Given object must be transient") 

3423 state.key = state.mapper._identity_key_from_state(state) 

3424 if state._deleted: 

3425 del state._deleted 

3426 state._commit_all(state.dict) 

3427 state._expire_attributes(state.dict, state.unloaded_expirable) 

3428 

3429 

3430def object_session(instance): 

3431 """Return the :class:`.Session` to which the given instance belongs. 

3432 

3433 This is essentially the same as the :attr:`.InstanceState.session` 

3434 accessor. See that attribute for details. 

3435 

3436 """ 

3437 

3438 try: 

3439 state = attributes.instance_state(instance) 

3440 except exc.NO_STATE as err: 

3441 util.raise_( 

3442 exc.UnmappedInstanceError(instance), replace_context=err, 

3443 ) 

3444 else: 

3445 return _state_session(state) 

3446 

3447 

3448_new_sessionid = util.counter()