Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/session.py: 19%

1201 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1# orm/session.py 

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

3# <see AUTHORS file> 

4# 

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

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

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

8 

9 

10import itertools 

11import sys 

12import weakref 

13 

14from . import attributes 

15from . import context 

16from . import exc 

17from . import identity 

18from . import loading 

19from . import persistence 

20from . import query 

21from . import state as statelib 

22from .base import _class_to_mapper 

23from .base import _none_set 

24from .base import _state_mapper 

25from .base import instance_str 

26from .base import object_mapper 

27from .base import object_state 

28from .base import state_str 

29from .unitofwork import UOWTransaction 

30from .. import engine 

31from .. import exc as sa_exc 

32from .. import sql 

33from .. import util 

34from ..engine.util import TransactionalContext 

35from ..inspection import inspect 

36from ..sql import coercions 

37from ..sql import dml 

38from ..sql import roles 

39from ..sql import visitors 

40from ..sql.base import CompileState 

41from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

42 

43__all__ = [ 

44 "Session", 

45 "SessionTransaction", 

46 "sessionmaker", 

47 "ORMExecuteState", 

48 "close_all_sessions", 

49 "make_transient", 

50 "make_transient_to_detached", 

51 "object_session", 

52] 

53 

54_sessions = weakref.WeakValueDictionary() 

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

56""" 

57 

58statelib._sessions = _sessions 

59 

60 

61def _state_session(state): 

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

63 associated, if any. 

64 """ 

65 return state.session 

66 

67 

68class _SessionClassMethods(object): 

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

70 

71 @classmethod 

72 @util.deprecated( 

73 "1.3", 

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

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

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

77 ) 

78 def close_all(cls): 

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

80 

81 close_all_sessions() 

82 

83 @classmethod 

84 @util.preload_module("sqlalchemy.orm.util") 

85 def identity_key(cls, *args, **kwargs): 

86 """Return an identity key. 

87 

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

89 

90 """ 

91 return util.preloaded.orm_util.identity_key(*args, **kwargs) 

92 

93 @classmethod 

94 def object_session(cls, instance): 

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

96 

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

98 

99 """ 

100 

101 return object_session(instance) 

102 

103 

104ACTIVE = util.symbol("ACTIVE") 

105PREPARED = util.symbol("PREPARED") 

106COMMITTED = util.symbol("COMMITTED") 

107DEACTIVE = util.symbol("DEACTIVE") 

108CLOSED = util.symbol("CLOSED") 

109 

110 

111class ORMExecuteState(util.MemoizedSlots): 

112 """Represents a call to the :meth:`_orm.Session.execute` method, as passed 

113 to the :meth:`.SessionEvents.do_orm_execute` event hook. 

114 

115 .. versionadded:: 1.4 

116 

117 .. seealso:: 

118 

119 :ref:`session_execute_events` - top level documentation on how 

120 to use :meth:`_orm.SessionEvents.do_orm_execute` 

121 

122 """ 

123 

124 __slots__ = ( 

125 "session", 

126 "statement", 

127 "parameters", 

128 "execution_options", 

129 "local_execution_options", 

130 "bind_arguments", 

131 "_compile_state_cls", 

132 "_starting_event_idx", 

133 "_events_todo", 

134 "_update_execution_options", 

135 ) 

136 

137 def __init__( 

138 self, 

139 session, 

140 statement, 

141 parameters, 

142 execution_options, 

143 bind_arguments, 

144 compile_state_cls, 

145 events_todo, 

146 ): 

147 self.session = session 

148 self.statement = statement 

149 self.parameters = parameters 

150 self.local_execution_options = execution_options 

151 self.execution_options = statement._execution_options.union( 

152 execution_options 

153 ) 

154 self.bind_arguments = bind_arguments 

155 self._compile_state_cls = compile_state_cls 

156 self._events_todo = list(events_todo) 

157 

158 def _remaining_events(self): 

159 return self._events_todo[self._starting_event_idx + 1 :] 

160 

161 def invoke_statement( 

162 self, 

163 statement=None, 

164 params=None, 

165 execution_options=None, 

166 bind_arguments=None, 

167 ): 

168 """Execute the statement represented by this 

169 :class:`.ORMExecuteState`, without re-invoking events that have 

170 already proceeded. 

171 

172 This method essentially performs a re-entrant execution of the current 

173 statement for which the :meth:`.SessionEvents.do_orm_execute` event is 

174 being currently invoked. The use case for this is for event handlers 

175 that want to override how the ultimate 

176 :class:`_engine.Result` object is returned, such as for schemes that 

177 retrieve results from an offline cache or which concatenate results 

178 from multiple executions. 

179 

180 When the :class:`_engine.Result` object is returned by the actual 

181 handler function within :meth:`_orm.SessionEvents.do_orm_execute` and 

182 is propagated to the calling 

183 :meth:`_orm.Session.execute` method, the remainder of the 

184 :meth:`_orm.Session.execute` method is preempted and the 

185 :class:`_engine.Result` object is returned to the caller of 

186 :meth:`_orm.Session.execute` immediately. 

187 

188 :param statement: optional statement to be invoked, in place of the 

189 statement currently represented by :attr:`.ORMExecuteState.statement`. 

190 

191 :param params: optional dictionary of parameters which will be merged 

192 into the existing :attr:`.ORMExecuteState.parameters` of this 

193 :class:`.ORMExecuteState`. 

194 

195 :param execution_options: optional dictionary of execution options 

196 will be merged into the existing 

197 :attr:`.ORMExecuteState.execution_options` of this 

198 :class:`.ORMExecuteState`. 

199 

200 :param bind_arguments: optional dictionary of bind_arguments 

201 which will be merged amongst the current 

202 :attr:`.ORMExecuteState.bind_arguments` 

203 of this :class:`.ORMExecuteState`. 

204 

205 :return: a :class:`_engine.Result` object with ORM-level results. 

206 

207 .. seealso:: 

208 

209 :ref:`do_orm_execute_re_executing` - background and examples on the 

210 appropriate usage of :meth:`_orm.ORMExecuteState.invoke_statement`. 

211 

212 

213 """ 

214 

215 if statement is None: 

216 statement = self.statement 

217 

218 _bind_arguments = dict(self.bind_arguments) 

219 if bind_arguments: 

220 _bind_arguments.update(bind_arguments) 

221 _bind_arguments["_sa_skip_events"] = True 

222 

223 if params: 

224 _params = dict(self.parameters) 

225 _params.update(params) 

226 else: 

227 _params = self.parameters 

228 

229 _execution_options = self.local_execution_options 

230 if execution_options: 

231 _execution_options = _execution_options.union(execution_options) 

232 

233 return self.session.execute( 

234 statement, 

235 _params, 

236 _execution_options, 

237 _bind_arguments, 

238 _parent_execute_state=self, 

239 ) 

240 

241 @property 

242 def bind_mapper(self): 

243 """Return the :class:`_orm.Mapper` that is the primary "bind" mapper. 

244 

245 For an :class:`_orm.ORMExecuteState` object invoking an ORM 

246 statement, that is, the :attr:`_orm.ORMExecuteState.is_orm_statement` 

247 attribute is ``True``, this attribute will return the 

248 :class:`_orm.Mapper` that is considered to be the "primary" mapper 

249 of the statement. The term "bind mapper" refers to the fact that 

250 a :class:`_orm.Session` object may be "bound" to multiple 

251 :class:`_engine.Engine` objects keyed to mapped classes, and the 

252 "bind mapper" determines which of those :class:`_engine.Engine` objects 

253 would be selected. 

254 

255 For a statement that is invoked against a single mapped class, 

256 :attr:`_orm.ORMExecuteState.bind_mapper` is intended to be a reliable 

257 way of getting this mapper. 

258 

259 .. versionadded:: 1.4.0b2 

260 

261 .. seealso:: 

262 

263 :attr:`_orm.ORMExecuteState.all_mappers` 

264 

265 

266 """ 

267 return self.bind_arguments.get("mapper", None) 

268 

269 @property 

270 def all_mappers(self): 

271 """Return a sequence of all :class:`_orm.Mapper` objects that are 

272 involved at the top level of this statement. 

273 

274 By "top level" we mean those :class:`_orm.Mapper` objects that would 

275 be represented in the result set rows for a :func:`_sql.select` 

276 query, or for a :func:`_dml.update` or :func:`_dml.delete` query, 

277 the mapper that is the main subject of the UPDATE or DELETE. 

278 

279 .. versionadded:: 1.4.0b2 

280 

281 .. seealso:: 

282 

283 :attr:`_orm.ORMExecuteState.bind_mapper` 

284 

285 

286 

287 """ 

288 if not self.is_orm_statement: 

289 return [] 

290 elif self.is_select: 

291 result = [] 

292 seen = set() 

293 for d in self.statement.column_descriptions: 

294 ent = d["entity"] 

295 if ent: 

296 insp = inspect(ent, raiseerr=False) 

297 if insp and insp.mapper and insp.mapper not in seen: 

298 seen.add(insp.mapper) 

299 result.append(insp.mapper) 

300 return result 

301 elif self.is_update or self.is_delete: 

302 return [self.bind_mapper] 

303 else: 

304 return [] 

305 

306 @property 

307 def is_orm_statement(self): 

308 """return True if the operation is an ORM statement. 

309 

310 This indicates that the select(), update(), or delete() being 

311 invoked contains ORM entities as subjects. For a statement 

312 that does not have ORM entities and instead refers only to 

313 :class:`.Table` metadata, it is invoked as a Core SQL statement 

314 and no ORM-level automation takes place. 

315 

316 """ 

317 return self._compile_state_cls is not None 

318 

319 @property 

320 def is_select(self): 

321 """return True if this is a SELECT operation.""" 

322 return self.statement.is_select 

323 

324 @property 

325 def is_insert(self): 

326 """return True if this is an INSERT operation.""" 

327 return self.statement.is_dml and self.statement.is_insert 

328 

329 @property 

330 def is_update(self): 

331 """return True if this is an UPDATE operation.""" 

332 return self.statement.is_dml and self.statement.is_update 

333 

334 @property 

335 def is_delete(self): 

336 """return True if this is a DELETE operation.""" 

337 return self.statement.is_dml and self.statement.is_delete 

338 

339 @property 

340 def _is_crud(self): 

341 return isinstance(self.statement, (dml.Update, dml.Delete)) 

342 

343 def update_execution_options(self, **opts): 

344 # TODO: no coverage 

345 self.local_execution_options = self.local_execution_options.union(opts) 

346 

347 def _orm_compile_options(self): 

348 if not self.is_select: 

349 return None 

350 try: 

351 opts = self.statement._compile_options 

352 except AttributeError: 

353 return None 

354 if opts.isinstance(context.ORMCompileState.default_compile_options): 

355 return opts 

356 else: 

357 return None 

358 

359 @property 

360 def lazy_loaded_from(self): 

361 """An :class:`.InstanceState` that is using this statement execution 

362 for a lazy load operation. 

363 

364 The primary rationale for this attribute is to support the horizontal 

365 sharding extension, where it is available within specific query 

366 execution time hooks created by this extension. To that end, the 

367 attribute is only intended to be meaningful at **query execution 

368 time**, and importantly not any time prior to that, including query 

369 compilation time. 

370 

371 """ 

372 return self.load_options._lazy_loaded_from 

373 

374 @property 

375 def loader_strategy_path(self): 

376 """Return the :class:`.PathRegistry` for the current load path. 

377 

378 This object represents the "path" in a query along relationships 

379 when a particular object or collection is being loaded. 

380 

381 """ 

382 opts = self._orm_compile_options() 

383 if opts is not None: 

384 return opts._current_path 

385 else: 

386 return None 

387 

388 @property 

389 def is_column_load(self): 

390 """Return True if the operation is refreshing column-oriented 

391 attributes on an existing ORM object. 

392 

393 This occurs during operations such as :meth:`_orm.Session.refresh`, 

394 as well as when an attribute deferred by :func:`_orm.defer` is 

395 being loaded, or an attribute that was expired either directly 

396 by :meth:`_orm.Session.expire` or via a commit operation is being 

397 loaded. 

398 

399 Handlers will very likely not want to add any options to queries 

400 when such an operation is occurring as the query should be a straight 

401 primary key fetch which should not have any additional WHERE criteria, 

402 and loader options travelling with the instance 

403 will have already been added to the query. 

404 

405 .. versionadded:: 1.4.0b2 

406 

407 .. seealso:: 

408 

409 :attr:`_orm.ORMExecuteState.is_relationship_load` 

410 

411 """ 

412 opts = self._orm_compile_options() 

413 return opts is not None and opts._for_refresh_state 

414 

415 @property 

416 def is_relationship_load(self): 

417 """Return True if this load is loading objects on behalf of a 

418 relationship. 

419 

420 This means, the loader in effect is either a LazyLoader, 

421 SelectInLoader, SubqueryLoader, or similar, and the entire 

422 SELECT statement being emitted is on behalf of a relationship 

423 load. 

424 

425 Handlers will very likely not want to add any options to queries 

426 when such an operation is occurring, as loader options are already 

427 capable of being propagated to relationship loaders and should 

428 be already present. 

429 

430 .. seealso:: 

431 

432 :attr:`_orm.ORMExecuteState.is_column_load` 

433 

434 """ 

435 opts = self._orm_compile_options() 

436 if opts is None: 

437 return False 

438 path = self.loader_strategy_path 

439 return path is not None and not path.is_root 

440 

441 @property 

442 def load_options(self): 

443 """Return the load_options that will be used for this execution.""" 

444 

445 if not self.is_select: 

446 raise sa_exc.InvalidRequestError( 

447 "This ORM execution is not against a SELECT statement " 

448 "so there are no load options." 

449 ) 

450 return self.execution_options.get( 

451 "_sa_orm_load_options", context.QueryContext.default_load_options 

452 ) 

453 

454 @property 

455 def update_delete_options(self): 

456 """Return the update_delete_options that will be used for this 

457 execution.""" 

458 

459 if not self._is_crud: 

460 raise sa_exc.InvalidRequestError( 

461 "This ORM execution is not against an UPDATE or DELETE " 

462 "statement so there are no update options." 

463 ) 

464 return self.execution_options.get( 

465 "_sa_orm_update_options", 

466 persistence.BulkUDCompileState.default_update_options, 

467 ) 

468 

469 @property 

470 def user_defined_options(self): 

471 """The sequence of :class:`.UserDefinedOptions` that have been 

472 associated with the statement being invoked. 

473 

474 """ 

475 return [ 

476 opt 

477 for opt in self.statement._with_options 

478 if not opt._is_compile_state and not opt._is_legacy_option 

479 ] 

480 

481 

482class SessionTransaction(TransactionalContext): 

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

484 

485 :class:`.SessionTransaction` is produced from the 

486 :meth:`_orm.Session.begin` 

487 and :meth:`_orm.Session.begin_nested` methods. It's largely an internal 

488 object that in modern use provides a context manager for session 

489 transactions. 

490 

491 Documentation on interacting with :class:`_orm.SessionTransaction` is 

492 at: :ref:`unitofwork_transaction`. 

493 

494 

495 .. versionchanged:: 1.4 The scoping and API methods to work with the 

496 :class:`_orm.SessionTransaction` object directly have been simplified. 

497 

498 .. seealso:: 

499 

500 :ref:`unitofwork_transaction` 

501 

502 :meth:`.Session.begin` 

503 

504 :meth:`.Session.begin_nested` 

505 

506 :meth:`.Session.rollback` 

507 

508 :meth:`.Session.commit` 

509 

510 :meth:`.Session.in_transaction` 

511 

512 :meth:`.Session.in_nested_transaction` 

513 

514 :meth:`.Session.get_transaction` 

515 

516 :meth:`.Session.get_nested_transaction` 

517 

518 

519 """ 

520 

521 _rollback_exception = None 

522 

523 def __init__( 

524 self, 

525 session, 

526 parent=None, 

527 nested=False, 

528 autobegin=False, 

529 ): 

530 TransactionalContext._trans_ctx_check(session) 

531 

532 self.session = session 

533 self._connections = {} 

534 self._parent = parent 

535 self.nested = nested 

536 if nested: 

537 self._previous_nested_transaction = session._nested_transaction 

538 self._state = ACTIVE 

539 if not parent and nested: 

540 raise sa_exc.InvalidRequestError( 

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

542 "transaction is in progress" 

543 ) 

544 

545 self._take_snapshot(autobegin=autobegin) 

546 

547 # make sure transaction is assigned before we call the 

548 # dispatch 

549 self.session._transaction = self 

550 

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

552 

553 @property 

554 def parent(self): 

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

556 :class:`.SessionTransaction`. 

557 

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

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

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

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

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

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

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

565 

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

567 

568 """ 

569 return self._parent 

570 

571 nested = False 

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

573 

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

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

576 

577 """ 

578 

579 @property 

580 def is_active(self): 

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

582 

583 def _assert_active( 

584 self, 

585 prepared_ok=False, 

586 rollback_ok=False, 

587 deactive_ok=False, 

588 closed_msg="This transaction is closed", 

589 ): 

590 if self._state is COMMITTED: 

591 raise sa_exc.InvalidRequestError( 

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

593 "SQL can be emitted within this transaction." 

594 ) 

595 elif self._state is PREPARED: 

596 if not prepared_ok: 

597 raise sa_exc.InvalidRequestError( 

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

599 "SQL can be emitted within this transaction." 

600 ) 

601 elif self._state is DEACTIVE: 

602 if not deactive_ok and not rollback_ok: 

603 if self._rollback_exception: 

604 raise sa_exc.PendingRollbackError( 

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

606 "due to a previous exception during flush." 

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

608 "first issue Session.rollback()." 

609 " Original exception was: %s" 

610 % self._rollback_exception, 

611 code="7s2a", 

612 ) 

613 elif not deactive_ok: 

614 raise sa_exc.InvalidRequestError( 

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

616 "SQL transaction being rolled back; no further " 

617 "SQL can be emitted within this transaction." 

618 ) 

619 elif self._state is CLOSED: 

620 raise sa_exc.ResourceClosedError(closed_msg) 

621 

622 @property 

623 def _is_transaction_boundary(self): 

624 return self.nested or not self._parent 

625 

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

627 self._assert_active() 

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

629 return self._connection_for_bind(bind, execution_options) 

630 

631 def _begin(self, nested=False): 

632 self._assert_active() 

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

634 

635 def _iterate_self_and_parents(self, upto=None): 

636 

637 current = self 

638 result = () 

639 while current: 

640 result += (current,) 

641 if current._parent is upto: 

642 break 

643 elif current._parent is None: 

644 raise sa_exc.InvalidRequestError( 

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

646 % (upto) 

647 ) 

648 else: 

649 current = current._parent 

650 

651 return result 

652 

653 def _take_snapshot(self, autobegin=False): 

654 if not self._is_transaction_boundary: 

655 self._new = self._parent._new 

656 self._deleted = self._parent._deleted 

657 self._dirty = self._parent._dirty 

658 self._key_switches = self._parent._key_switches 

659 return 

660 

661 if not autobegin and not self.session._flushing: 

662 self.session.flush() 

663 

664 self._new = weakref.WeakKeyDictionary() 

665 self._deleted = weakref.WeakKeyDictionary() 

666 self._dirty = weakref.WeakKeyDictionary() 

667 self._key_switches = weakref.WeakKeyDictionary() 

668 

669 def _restore_snapshot(self, dirty_only=False): 

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

671 

672 Corresponds to a rollback. 

673 

674 """ 

675 assert self._is_transaction_boundary 

676 

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

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

679 

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

681 # we probably can do this conditionally based on 

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

683 self.session.identity_map.safe_discard(s) 

684 

685 # restore the old key 

686 s.key = oldkey 

687 

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

689 if s not in to_expunge: 

690 self.session.identity_map.replace(s) 

691 

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

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

694 

695 assert not self.session._deleted 

696 

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

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

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

700 

701 def _remove_snapshot(self): 

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

703 

704 Corresponds to a commit. 

705 

706 """ 

707 assert self._is_transaction_boundary 

708 

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

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

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

712 

713 statelib.InstanceState._detach_states( 

714 list(self._deleted), self.session 

715 ) 

716 self._deleted.clear() 

717 elif self.nested: 

718 self._parent._new.update(self._new) 

719 self._parent._dirty.update(self._dirty) 

720 self._parent._deleted.update(self._deleted) 

721 self._parent._key_switches.update(self._key_switches) 

722 

723 def _connection_for_bind(self, bind, execution_options): 

724 self._assert_active() 

725 

726 if bind in self._connections: 

727 if execution_options: 

728 util.warn( 

729 "Connection is already established for the " 

730 "given bind; execution_options ignored" 

731 ) 

732 return self._connections[bind][0] 

733 

734 local_connect = False 

735 should_commit = True 

736 

737 if self._parent: 

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

739 if not self.nested: 

740 return conn 

741 else: 

742 if isinstance(bind, engine.Connection): 

743 conn = bind 

744 if conn.engine in self._connections: 

745 raise sa_exc.InvalidRequestError( 

746 "Session already has a Connection associated for the " 

747 "given Connection's Engine" 

748 ) 

749 else: 

750 conn = bind.connect() 

751 local_connect = True 

752 

753 try: 

754 if execution_options: 

755 conn = conn.execution_options(**execution_options) 

756 

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

758 transaction = conn.begin_twophase() 

759 elif self.nested: 

760 transaction = conn.begin_nested() 

761 elif conn.in_transaction(): 

762 # if given a future connection already in a transaction, don't 

763 # commit that transaction unless it is a savepoint 

764 if conn.in_nested_transaction(): 

765 transaction = conn.get_nested_transaction() 

766 else: 

767 transaction = conn.get_transaction() 

768 should_commit = False 

769 else: 

770 transaction = conn.begin() 

771 except: 

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

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

774 if local_connect: 

775 conn.close() 

776 raise 

777 else: 

778 bind_is_connection = isinstance(bind, engine.Connection) 

779 

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

781 conn, 

782 transaction, 

783 should_commit, 

784 not bind_is_connection, 

785 ) 

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

787 return conn 

788 

789 def prepare(self): 

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

791 raise sa_exc.InvalidRequestError( 

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

793 "can't prepare." 

794 ) 

795 self._prepare_impl() 

796 

797 def _prepare_impl(self): 

798 self._assert_active() 

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

800 self.session.dispatch.before_commit(self.session) 

801 

802 stx = self.session._transaction 

803 if stx is not self: 

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

805 subtransaction.commit() 

806 

807 if not self.session._flushing: 

808 for _flush_guard in range(100): 

809 if self.session._is_clean(): 

810 break 

811 self.session.flush() 

812 else: 

813 raise exc.FlushError( 

814 "Over 100 subsequent flushes have occurred within " 

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

816 "creating new objects?" 

817 ) 

818 

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

820 try: 

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

822 t[1].prepare() 

823 except: 

824 with util.safe_reraise(): 

825 self.rollback() 

826 

827 self._state = PREPARED 

828 

829 def commit(self, _to_root=False): 

830 self._assert_active(prepared_ok=True) 

831 if self._state is not PREPARED: 

832 self._prepare_impl() 

833 

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

835 for conn, trans, should_commit, autoclose in set( 

836 self._connections.values() 

837 ): 

838 if should_commit: 

839 trans.commit() 

840 

841 self._state = COMMITTED 

842 self.session.dispatch.after_commit(self.session) 

843 

844 self._remove_snapshot() 

845 

846 self.close() 

847 

848 if _to_root and self._parent: 

849 return self._parent.commit(_to_root=True) 

850 

851 return self._parent 

852 

853 def rollback(self, _capture_exception=False, _to_root=False): 

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

855 

856 stx = self.session._transaction 

857 if stx is not self: 

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

859 subtransaction.close() 

860 

861 boundary = self 

862 rollback_err = None 

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

864 for transaction in self._iterate_self_and_parents(): 

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

866 try: 

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

868 t[1].rollback() 

869 

870 transaction._state = DEACTIVE 

871 self.session.dispatch.after_rollback(self.session) 

872 except: 

873 rollback_err = sys.exc_info() 

874 finally: 

875 transaction._state = DEACTIVE 

876 transaction._restore_snapshot( 

877 dirty_only=transaction.nested 

878 ) 

879 boundary = transaction 

880 break 

881 else: 

882 transaction._state = DEACTIVE 

883 

884 sess = self.session 

885 

886 if not rollback_err and not sess._is_clean(): 

887 

888 # if items were added, deleted, or mutated 

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

890 util.warn( 

891 "Session's state has been changed on " 

892 "a non-active transaction - this state " 

893 "will be discarded." 

894 ) 

895 boundary._restore_snapshot(dirty_only=boundary.nested) 

896 

897 self.close() 

898 

899 if self._parent and _capture_exception: 

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

901 

902 if rollback_err: 

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

904 

905 sess.dispatch.after_soft_rollback(sess, self) 

906 

907 if _to_root and self._parent: 

908 return self._parent.rollback(_to_root=True) 

909 return self._parent 

910 

911 def close(self, invalidate=False): 

912 if self.nested: 

913 self.session._nested_transaction = ( 

914 self._previous_nested_transaction 

915 ) 

916 

917 self.session._transaction = self._parent 

918 

919 if self._parent is None: 

920 for connection, transaction, should_commit, autoclose in set( 

921 self._connections.values() 

922 ): 

923 if invalidate: 

924 connection.invalidate() 

925 if should_commit and transaction.is_active: 

926 transaction.close() 

927 if autoclose: 

928 connection.close() 

929 

930 self._state = CLOSED 

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

932 

933 self.session = None 

934 self._connections = None 

935 

936 def _get_subject(self): 

937 return self.session 

938 

939 def _transaction_is_active(self): 

940 return self._state is ACTIVE 

941 

942 def _transaction_is_closed(self): 

943 return self._state is CLOSED 

944 

945 def _rollback_can_be_called(self): 

946 return self._state not in (COMMITTED, CLOSED) 

947 

948 

949class Session(_SessionClassMethods): 

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

951 

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

953 

954 

955 """ 

956 

957 _is_asyncio = False 

958 

959 @util.deprecated_params( 

960 autocommit=( 

961 "2.0", 

962 "The :paramref:`.Session.autocommit` parameter is deprecated " 

963 "and will be removed in SQLAlchemy version 2.0. The " 

964 ':class:`_orm.Session` now features "autobegin" behavior ' 

965 "such that the :meth:`.Session.begin` method may be called " 

966 "if a transaction has not yet been started yet. See the section " 

967 ":ref:`session_explicit_begin` for background.", 

968 ), 

969 ) 

970 def __init__( 

971 self, 

972 bind=None, 

973 autoflush=True, 

974 future=False, 

975 expire_on_commit=True, 

976 autocommit=False, 

977 twophase=False, 

978 binds=None, 

979 enable_baked_queries=True, 

980 info=None, 

981 query_cls=None, 

982 ): 

983 r"""Construct a new Session. 

984 

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

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

987 set of arguments. 

988 

989 :param autocommit: 

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

991 :class:`.Session` does not automatically begin transactions for 

992 individual statement executions, will acquire connections from the 

993 engine on an as-needed basis, releasing to the connection pool 

994 after each statement. Flushes will begin and commit (or possibly 

995 rollback) their own transaction if no transaction is present. 

996 When using this mode, the 

997 :meth:`.Session.begin` method may be used to explicitly start 

998 transactions, but the usual "autobegin" behavior is not present. 

999 

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

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

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

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

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

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

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

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

1008 

1009 .. seealso:: 

1010 

1011 :ref:`session_flushing` - additional background on autoflush 

1012 

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

1014 :class:`_engine.Connection` to 

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

1016 operations performed by this session will execute via this 

1017 connectable. 

1018 

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

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

1021 objects as the source of 

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

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

1024 arbitrary Python classes that are bases for mapped classes, 

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

1026 The 

1027 values of the dictionary are then instances of 

1028 :class:`_engine.Engine` 

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

1030 Operations which 

1031 proceed relative to a particular mapped class will consult this 

1032 dictionary for the closest matching entity in order to determine 

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

1034 operation. The complete heuristics for resolution are 

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

1036 

1037 Session = sessionmaker(binds={ 

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

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

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

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

1042 }) 

1043 

1044 .. seealso:: 

1045 

1046 :ref:`session_partitioning` 

1047 

1048 :meth:`.Session.bind_mapper` 

1049 

1050 :meth:`.Session.bind_table` 

1051 

1052 :meth:`.Session.get_bind` 

1053 

1054 

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

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

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

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

1059 constructor for ``Session``. 

1060 

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

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

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

1064 of this extension. When set to ``False``, caching as used by 

1065 this particular extension is disabled. 

1066 

1067 .. versionchanged:: 1.4 The ``sqlalchemy.ext.baked`` extension is 

1068 legacy and is not used by any of SQLAlchemy's internals. This 

1069 flag therefore only affects applications that are making explicit 

1070 use of this extension within their own code. 

1071 

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

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

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

1075 transaction will load from the most recent database state. 

1076 

1077 .. seealso:: 

1078 

1079 :ref:`session_committing` 

1080 

1081 :param future: if True, use 2.0 style transactional and engine 

1082 behavior. Future mode includes the following behaviors: 

1083 

1084 * The :class:`_orm.Session` will not use "bound" metadata in order 

1085 to locate an :class:`_engine.Engine`; the engine or engines in use 

1086 must be specified to the constructor of :class:`_orm.Session` or 

1087 otherwise be configured against the :class:`_orm.sessionmaker` 

1088 in use 

1089 

1090 * The "subtransactions" feature of :meth:`_orm.Session.begin` is 

1091 removed in version 2.0 and is disabled when the future flag is 

1092 set. 

1093 

1094 * The behavior of the :paramref:`_orm.relationship.cascade_backrefs` 

1095 flag on a :func:`_orm.relationship` will always assume 

1096 "False" behavior. 

1097 

1098 .. versionadded:: 1.4 

1099 

1100 .. seealso:: 

1101 

1102 :ref:`migration_20_toplevel` 

1103 

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

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

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

1107 construction time so that modifications to the per- 

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

1109 :class:`.Session`. 

1110 

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

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

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

1114 

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

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

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

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

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

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

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

1122 transaction, before each transaction is committed. 

1123 

1124 """ 

1125 self.identity_map = identity.WeakInstanceDict() 

1126 

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

1128 self._deleted = {} # same 

1129 self.bind = bind 

1130 self.__binds = {} 

1131 self._flushing = False 

1132 self._warn_on_events = False 

1133 self._transaction = None 

1134 self._nested_transaction = None 

1135 self.future = future 

1136 self.hash_key = _new_sessionid() 

1137 self.autoflush = autoflush 

1138 self.expire_on_commit = expire_on_commit 

1139 self.enable_baked_queries = enable_baked_queries 

1140 

1141 if autocommit: 

1142 if future: 

1143 raise sa_exc.ArgumentError( 

1144 "Cannot use autocommit mode with future=True." 

1145 ) 

1146 self.autocommit = True 

1147 else: 

1148 self.autocommit = False 

1149 

1150 self.twophase = twophase 

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

1152 if info: 

1153 self.info.update(info) 

1154 

1155 if binds is not None: 

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

1157 self._add_bind(key, bind) 

1158 

1159 _sessions[self.hash_key] = self 

1160 

1161 # used by sqlalchemy.engine.util.TransactionalContext 

1162 _trans_context_manager = None 

1163 

1164 connection_callable = None 

1165 

1166 def __enter__(self): 

1167 return self 

1168 

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

1170 self.close() 

1171 

1172 @util.contextmanager 

1173 def _maker_context_manager(self): 

1174 with self: 

1175 with self.begin(): 

1176 yield self 

1177 

1178 @property 

1179 @util.deprecated_20( 

1180 ":attr:`_orm.Session.transaction`", 

1181 alternative="For context manager use, use " 

1182 ":meth:`_orm.Session.begin`. To access " 

1183 "the current root transaction, use " 

1184 ":meth:`_orm.Session.get_transaction`.", 

1185 warn_on_attribute_access=True, 

1186 ) 

1187 def transaction(self): 

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

1189 

1190 May be None if no transaction has begun yet. 

1191 

1192 .. versionchanged:: 1.4 the :attr:`.Session.transaction` attribute 

1193 is now a read-only descriptor that also may return None if no 

1194 transaction has begun yet. 

1195 

1196 

1197 """ 

1198 return self._legacy_transaction() 

1199 

1200 def _legacy_transaction(self): 

1201 if not self.future: 

1202 self._autobegin() 

1203 return self._transaction 

1204 

1205 def in_transaction(self): 

1206 """Return True if this :class:`_orm.Session` has begun a transaction. 

1207 

1208 .. versionadded:: 1.4 

1209 

1210 .. seealso:: 

1211 

1212 :attr:`_orm.Session.is_active` 

1213 

1214 

1215 """ 

1216 return self._transaction is not None 

1217 

1218 def in_nested_transaction(self): 

1219 """Return True if this :class:`_orm.Session` has begun a nested 

1220 transaction, e.g. SAVEPOINT. 

1221 

1222 .. versionadded:: 1.4 

1223 

1224 """ 

1225 return self._nested_transaction is not None 

1226 

1227 def get_transaction(self): 

1228 """Return the current root transaction in progress, if any. 

1229 

1230 .. versionadded:: 1.4 

1231 

1232 """ 

1233 trans = self._transaction 

1234 while trans is not None and trans._parent is not None: 

1235 trans = trans._parent 

1236 return trans 

1237 

1238 def get_nested_transaction(self): 

1239 """Return the current nested transaction in progress, if any. 

1240 

1241 .. versionadded:: 1.4 

1242 

1243 """ 

1244 

1245 return self._nested_transaction 

1246 

1247 @util.memoized_property 

1248 def info(self): 

1249 """A user-modifiable dictionary. 

1250 

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

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

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

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

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

1256 

1257 """ 

1258 return {} 

1259 

1260 def _autobegin(self): 

1261 if not self.autocommit and self._transaction is None: 

1262 

1263 trans = SessionTransaction(self, autobegin=True) 

1264 assert self._transaction is trans 

1265 return True 

1266 

1267 return False 

1268 

1269 @util.deprecated_params( 

1270 subtransactions=( 

1271 "2.0", 

1272 "The :paramref:`_orm.Session.begin.subtransactions` flag is " 

1273 "deprecated and " 

1274 "will be removed in SQLAlchemy version 2.0. See " 

1275 "the documentation at :ref:`session_subtransactions` for " 

1276 "background on a compatible alternative pattern.", 

1277 ) 

1278 ) 

1279 def begin(self, subtransactions=False, nested=False, _subtrans=False): 

1280 """Begin a transaction, or nested transaction, 

1281 on this :class:`.Session`, if one is not already begun. 

1282 

1283 The :class:`_orm.Session` object features **autobegin** behavior, 

1284 so that normally it is not necessary to call the 

1285 :meth:`_orm.Session.begin` 

1286 method explicitly. However, it may be used in order to control 

1287 the scope of when the transactional state is begun. 

1288 

1289 When used to begin the outermost transaction, an error is raised 

1290 if this :class:`.Session` is already inside of a transaction. 

1291 

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

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

1294 documentation on SAVEPOINT transactions, please see 

1295 :ref:`session_begin_nested`. 

1296 

1297 :param subtransactions: if True, indicates that this 

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

1299 

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

1301 :class:`.SessionTransaction` 

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

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

1304 an example. 

1305 

1306 .. seealso:: 

1307 

1308 :ref:`session_autobegin` 

1309 

1310 :ref:`unitofwork_transaction` 

1311 

1312 :meth:`.Session.begin_nested` 

1313 

1314 

1315 """ 

1316 

1317 if subtransactions and self.future: 

1318 raise NotImplementedError( 

1319 "subtransactions are not implemented in future " 

1320 "Session objects." 

1321 ) 

1322 

1323 if self._autobegin(): 

1324 if not subtransactions and not nested and not _subtrans: 

1325 return self._transaction 

1326 

1327 if self._transaction is not None: 

1328 if subtransactions or _subtrans or nested: 

1329 trans = self._transaction._begin(nested=nested) 

1330 assert self._transaction is trans 

1331 if nested: 

1332 self._nested_transaction = trans 

1333 else: 

1334 raise sa_exc.InvalidRequestError( 

1335 "A transaction is already begun on this Session." 

1336 ) 

1337 elif not self.autocommit: 

1338 # outermost transaction. must be a not nested and not 

1339 # a subtransaction 

1340 

1341 assert not nested and not _subtrans and not subtransactions 

1342 trans = SessionTransaction(self) 

1343 assert self._transaction is trans 

1344 else: 

1345 # legacy autocommit mode 

1346 assert not self.future 

1347 trans = SessionTransaction(self, nested=nested) 

1348 assert self._transaction is trans 

1349 

1350 return self._transaction # needed for __enter__/__exit__ hook 

1351 

1352 def begin_nested(self): 

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

1354 

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

1356 SAVEPOINT for this method to function correctly. 

1357 

1358 For documentation on SAVEPOINT 

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

1360 

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

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

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

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

1365 

1366 .. seealso:: 

1367 

1368 :ref:`session_begin_nested` 

1369 

1370 :ref:`pysqlite_serializable` - special workarounds required 

1371 with the SQLite driver in order for SAVEPOINT to work 

1372 correctly. 

1373 

1374 """ 

1375 return self.begin(nested=True) 

1376 

1377 def rollback(self): 

1378 """Rollback the current transaction in progress. 

1379 

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

1381 

1382 In :term:`1.x-style` use, this method rolls back the topmost 

1383 database transaction if no nested transactions are in effect, or 

1384 to the current nested transaction if one is in effect. 

1385 

1386 When 

1387 :term:`2.0-style` use is in effect via the 

1388 :paramref:`_orm.Session.future` flag, the method always rolls back 

1389 the topmost database transaction, discarding any nested 

1390 transactions that may be in progress. 

1391 

1392 .. seealso:: 

1393 

1394 :ref:`session_rollback` 

1395 

1396 :ref:`unitofwork_transaction` 

1397 

1398 """ 

1399 if self._transaction is None: 

1400 pass 

1401 else: 

1402 self._transaction.rollback(_to_root=self.future) 

1403 

1404 def commit(self): 

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

1406 

1407 When the COMMIT operation is complete, all objects are fully 

1408 :term:`expired`, erasing their internal contents, which will be 

1409 automatically re-loaded when the objects are next accessed. In the 

1410 interim, these objects are in an expired state and will not function if 

1411 they are :term:`detached` from the :class:`.Session`. Additionally, 

1412 this re-load operation is not supported when using asyncio-oriented 

1413 APIs. The :paramref:`.Session.expire_on_commit` parameter may be used 

1414 to disable this behavior. 

1415 

1416 When there is no transaction in place for the :class:`.Session`, 

1417 indicating that no operations were invoked on this :class:`.Session` 

1418 since the previous call to :meth:`.Session.commit`, the method will 

1419 begin and commit an internal-only "logical" transaction, that does not 

1420 normally affect the database unless pending flush changes were 

1421 detected, but will still invoke event handlers and object expiration 

1422 rules. 

1423 

1424 If :term:`1.x-style` use is in effect and there are currently 

1425 SAVEPOINTs in progress via :meth:`_orm.Session.begin_nested`, 

1426 the operation will release the current SAVEPOINT but not commit 

1427 the outermost database transaction. 

1428 

1429 If :term:`2.0-style` use is in effect via the 

1430 :paramref:`_orm.Session.future` flag, the outermost database 

1431 transaction is committed unconditionally, automatically releasing any 

1432 SAVEPOINTs in effect. 

1433 

1434 When using legacy "autocommit" mode, this method is only 

1435 valid to call if a transaction is actually in progress, else 

1436 an error is raised. Similarly, when using legacy "subtransactions", 

1437 the method will instead close out the current "subtransaction", 

1438 rather than the actual database transaction, if a transaction 

1439 is in progress. 

1440 

1441 .. seealso:: 

1442 

1443 :ref:`session_committing` 

1444 

1445 :ref:`unitofwork_transaction` 

1446 

1447 :ref:`asyncio_orm_avoid_lazyloads` 

1448 

1449 """ 

1450 if self._transaction is None: 

1451 if not self._autobegin(): 

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

1453 

1454 self._transaction.commit(_to_root=self.future) 

1455 

1456 def prepare(self): 

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

1458 

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

1460 :exc:`~sqlalchemy.exc.InvalidRequestError`. 

1461 

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

1463 current transaction is not such, an 

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

1465 

1466 """ 

1467 if self._transaction is None: 

1468 if not self._autobegin(): 

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

1470 

1471 self._transaction.prepare() 

1472 

1473 def connection( 

1474 self, 

1475 bind_arguments=None, 

1476 close_with_result=False, 

1477 execution_options=None, 

1478 **kw 

1479 ): 

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

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

1482 

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

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

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

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

1487 returned (note that no 

1488 transactional state is established with the DBAPI until the first 

1489 SQL statement is emitted). 

1490 

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

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

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

1494 :class:`_engine.Engine`. 

1495 

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

1497 resolved through any of the optional keyword arguments. This 

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

1499 

1500 :param bind_arguments: dictionary of bind arguments. May include 

1501 "mapper", "bind", "clause", other custom arguments that are passed 

1502 to :meth:`.Session.get_bind`. 

1503 

1504 :param bind: 

1505 deprecated; use bind_arguments 

1506 

1507 :param mapper: 

1508 deprecated; use bind_arguments 

1509 

1510 :param clause: 

1511 deprecated; use bind_arguments 

1512 

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

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

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

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

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

1518 transaction in progress. 

1519 

1520 .. deprecated:: 1.4 this parameter is deprecated and will be removed 

1521 in SQLAlchemy 2.0 

1522 

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

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

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

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

1527 the arguments are ignored. 

1528 

1529 .. seealso:: 

1530 

1531 :ref:`session_transaction_isolation` 

1532 

1533 :param \**kw: 

1534 deprecated; use bind_arguments 

1535 

1536 """ 

1537 

1538 if not bind_arguments: 

1539 bind_arguments = kw 

1540 

1541 bind = bind_arguments.pop("bind", None) 

1542 if bind is None: 

1543 bind = self.get_bind(**bind_arguments) 

1544 

1545 return self._connection_for_bind( 

1546 bind, 

1547 close_with_result=close_with_result, 

1548 execution_options=execution_options, 

1549 ) 

1550 

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

1552 TransactionalContext._trans_ctx_check(self) 

1553 

1554 if self._transaction is not None or self._autobegin(): 

1555 return self._transaction._connection_for_bind( 

1556 engine, execution_options 

1557 ) 

1558 

1559 assert self._transaction is None 

1560 assert self.autocommit 

1561 conn = engine.connect(**kw) 

1562 if execution_options: 

1563 conn = conn.execution_options(**execution_options) 

1564 return conn 

1565 

1566 def execute( 

1567 self, 

1568 statement, 

1569 params=None, 

1570 execution_options=util.EMPTY_DICT, 

1571 bind_arguments=None, 

1572 _parent_execute_state=None, 

1573 _add_event=None, 

1574 **kw 

1575 ): 

1576 r"""Execute a SQL expression construct. 

1577 

1578 Returns a :class:`_engine.Result` object representing 

1579 results of the statement execution. 

1580 

1581 E.g.:: 

1582 

1583 from sqlalchemy import select 

1584 result = session.execute( 

1585 select(User).where(User.id == 5) 

1586 ) 

1587 

1588 The API contract of :meth:`_orm.Session.execute` is similar to that 

1589 of :meth:`_future.Connection.execute`, the :term:`2.0 style` version 

1590 of :class:`_future.Connection`. 

1591 

1592 .. versionchanged:: 1.4 the :meth:`_orm.Session.execute` method is 

1593 now the primary point of ORM statement execution when using 

1594 :term:`2.0 style` ORM usage. 

1595 

1596 :param statement: 

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

1598 such as :func:`_expression.select`). 

1599 

1600 :param params: 

1601 Optional dictionary, or list of dictionaries, containing 

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

1603 execution occurs; if a list of dictionaries, an 

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

1605 must correspond to parameter names present in the statement. 

1606 

1607 :param execution_options: optional dictionary of execution options, 

1608 which will be associated with the statement execution. This 

1609 dictionary can provide a subset of the options that are accepted 

1610 by :meth:`_engine.Connection.execution_options`, and may also 

1611 provide additional options understood only in an ORM context. 

1612 

1613 :param bind_arguments: dictionary of additional arguments to determine 

1614 the bind. May include "mapper", "bind", or other custom arguments. 

1615 Contents of this dictionary are passed to the 

1616 :meth:`.Session.get_bind` method. 

1617 

1618 :param mapper: 

1619 deprecated; use the bind_arguments dictionary 

1620 

1621 :param bind: 

1622 deprecated; use the bind_arguments dictionary 

1623 

1624 :param \**kw: 

1625 deprecated; use the bind_arguments dictionary 

1626 

1627 :return: a :class:`_engine.Result` object. 

1628 

1629 

1630 """ 

1631 statement = coercions.expect(roles.StatementRole, statement) 

1632 

1633 if kw: 

1634 util.warn_deprecated_20( 

1635 "Passing bind arguments to Session.execute() as keyword " 

1636 "arguments is deprecated and will be removed SQLAlchemy 2.0. " 

1637 "Please use the bind_arguments parameter." 

1638 ) 

1639 if not bind_arguments: 

1640 bind_arguments = kw 

1641 else: 

1642 bind_arguments.update(kw) 

1643 elif not bind_arguments: 

1644 bind_arguments = {} 

1645 else: 

1646 bind_arguments = dict(bind_arguments) 

1647 

1648 if ( 

1649 statement._propagate_attrs.get("compile_state_plugin", None) 

1650 == "orm" 

1651 ): 

1652 # note that even without "future" mode, we need 

1653 compile_state_cls = CompileState._get_plugin_class_for_plugin( 

1654 statement, "orm" 

1655 ) 

1656 else: 

1657 compile_state_cls = None 

1658 

1659 execution_options = util.coerce_to_immutabledict(execution_options) 

1660 

1661 if compile_state_cls is not None: 

1662 ( 

1663 statement, 

1664 execution_options, 

1665 ) = compile_state_cls.orm_pre_session_exec( 

1666 self, 

1667 statement, 

1668 params, 

1669 execution_options, 

1670 bind_arguments, 

1671 _parent_execute_state is not None, 

1672 ) 

1673 else: 

1674 bind_arguments.setdefault("clause", statement) 

1675 execution_options = execution_options.union( 

1676 {"future_result": True} 

1677 ) 

1678 

1679 if _parent_execute_state: 

1680 events_todo = _parent_execute_state._remaining_events() 

1681 else: 

1682 events_todo = self.dispatch.do_orm_execute 

1683 if _add_event: 

1684 events_todo = list(events_todo) + [_add_event] 

1685 

1686 if events_todo: 

1687 orm_exec_state = ORMExecuteState( 

1688 self, 

1689 statement, 

1690 params, 

1691 execution_options, 

1692 bind_arguments, 

1693 compile_state_cls, 

1694 events_todo, 

1695 ) 

1696 for idx, fn in enumerate(events_todo): 

1697 orm_exec_state._starting_event_idx = idx 

1698 result = fn(orm_exec_state) 

1699 if result: 

1700 return result 

1701 

1702 statement = orm_exec_state.statement 

1703 execution_options = orm_exec_state.local_execution_options 

1704 

1705 bind = self.get_bind(**bind_arguments) 

1706 

1707 if self.autocommit: 

1708 # legacy stuff, we can't use future_result w/ autocommit because 

1709 # we rely upon close_with_result, also legacy. it's all 

1710 # interrelated 

1711 conn = self._connection_for_bind(bind, close_with_result=True) 

1712 execution_options = execution_options.union( 

1713 dict(future_result=False) 

1714 ) 

1715 else: 

1716 conn = self._connection_for_bind(bind) 

1717 result = conn._execute_20(statement, params or {}, execution_options) 

1718 

1719 if compile_state_cls: 

1720 result = compile_state_cls.orm_setup_cursor_result( 

1721 self, 

1722 statement, 

1723 params, 

1724 execution_options, 

1725 bind_arguments, 

1726 result, 

1727 ) 

1728 

1729 return result 

1730 

1731 def scalar( 

1732 self, 

1733 statement, 

1734 params=None, 

1735 execution_options=util.EMPTY_DICT, 

1736 bind_arguments=None, 

1737 **kw 

1738 ): 

1739 """Execute a statement and return a scalar result. 

1740 

1741 Usage and parameters are the same as that of 

1742 :meth:`_orm.Session.execute`; the return result is a scalar Python 

1743 value. 

1744 

1745 """ 

1746 

1747 return self.execute( 

1748 statement, 

1749 params=params, 

1750 execution_options=execution_options, 

1751 bind_arguments=bind_arguments, 

1752 **kw 

1753 ).scalar() 

1754 

1755 def scalars( 

1756 self, 

1757 statement, 

1758 params=None, 

1759 execution_options=util.EMPTY_DICT, 

1760 bind_arguments=None, 

1761 **kw 

1762 ): 

1763 """Execute a statement and return the results as scalars. 

1764 

1765 Usage and parameters are the same as that of 

1766 :meth:`_orm.Session.execute`; the return result is a 

1767 :class:`_result.ScalarResult` filtering object which 

1768 will return single elements rather than :class:`_row.Row` objects. 

1769 

1770 :return: a :class:`_result.ScalarResult` object 

1771 

1772 .. versionadded:: 1.4.24 Added :meth:`_orm.Session.scalars` 

1773 

1774 .. versionadded:: 1.4.26 Added :meth:`_orm.scoped_session.scalars` 

1775 

1776 """ 

1777 

1778 return self.execute( 

1779 statement, 

1780 params=params, 

1781 execution_options=execution_options, 

1782 bind_arguments=bind_arguments, 

1783 **kw 

1784 ).scalars() 

1785 

1786 def close(self): 

1787 """Close out the transactional resources and ORM objects used by this 

1788 :class:`_orm.Session`. 

1789 

1790 This expunges all ORM objects associated with this 

1791 :class:`_orm.Session`, ends any transaction in progress and 

1792 :term:`releases` any :class:`_engine.Connection` objects which this 

1793 :class:`_orm.Session` itself has checked out from associated 

1794 :class:`_engine.Engine` objects. The operation then leaves the 

1795 :class:`_orm.Session` in a state which it may be used again. 

1796 

1797 .. tip:: 

1798 

1799 The :meth:`_orm.Session.close` method **does not prevent the 

1800 Session from being used again**. The :class:`_orm.Session` itself 

1801 does not actually have a distinct "closed" state; it merely means 

1802 the :class:`_orm.Session` will release all database connections 

1803 and ORM objects. 

1804 

1805 .. versionchanged:: 1.4 The :meth:`.Session.close` method does not 

1806 immediately create a new :class:`.SessionTransaction` object; 

1807 instead, the new :class:`.SessionTransaction` is created only if 

1808 the :class:`.Session` is used again for a database operation. 

1809 

1810 .. seealso:: 

1811 

1812 :ref:`session_closing` - detail on the semantics of 

1813 :meth:`_orm.Session.close` 

1814 

1815 """ 

1816 self._close_impl(invalidate=False) 

1817 

1818 def invalidate(self): 

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

1820 

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

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

1823 method will be called on each :class:`_engine.Connection` object 

1824 that is currently in use for a transaction (typically there is only 

1825 one connection unless the :class:`_orm.Session` is used with 

1826 multiple engines). 

1827 

1828 This can be called when the database is known to be in a state where 

1829 the connections are no longer safe to be used. 

1830 

1831 Below illustrates a scenario when using `gevent 

1832 <https://www.gevent.org/>`_, which can produce ``Timeout`` exceptions 

1833 that may mean the underlying connection should be discarded:: 

1834 

1835 import gevent 

1836 

1837 try: 

1838 sess = Session() 

1839 sess.add(User()) 

1840 sess.commit() 

1841 except gevent.Timeout: 

1842 sess.invalidate() 

1843 raise 

1844 except: 

1845 sess.rollback() 

1846 raise 

1847 

1848 The method additionally does everything that :meth:`_orm.Session.close` 

1849 does, including that all ORM objects are expunged. 

1850 

1851 """ 

1852 self._close_impl(invalidate=True) 

1853 

1854 def _close_impl(self, invalidate): 

1855 self.expunge_all() 

1856 if self._transaction is not None: 

1857 for transaction in self._transaction._iterate_self_and_parents(): 

1858 transaction.close(invalidate) 

1859 

1860 def expunge_all(self): 

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

1862 

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

1864 ``Session``. 

1865 

1866 """ 

1867 

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

1869 self.identity_map._kill() 

1870 self.identity_map = identity.WeakInstanceDict() 

1871 self._new = {} 

1872 self._deleted = {} 

1873 

1874 statelib.InstanceState._detach_states(all_states, self) 

1875 

1876 def _add_bind(self, key, bind): 

1877 try: 

1878 insp = inspect(key) 

1879 except sa_exc.NoInspectionAvailable as err: 

1880 if not isinstance(key, type): 

1881 util.raise_( 

1882 sa_exc.ArgumentError( 

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

1884 ), 

1885 replace_context=err, 

1886 ) 

1887 else: 

1888 self.__binds[key] = bind 

1889 else: 

1890 if insp.is_selectable: 

1891 self.__binds[insp] = bind 

1892 elif insp.is_mapper: 

1893 self.__binds[insp.class_] = bind 

1894 for _selectable in insp._all_tables: 

1895 self.__binds[_selectable] = bind 

1896 else: 

1897 raise sa_exc.ArgumentError( 

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

1899 ) 

1900 

1901 def bind_mapper(self, mapper, bind): 

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

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

1904 :class:`_engine.Connection`. 

1905 

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

1907 :meth:`.Session.get_bind` method. 

1908 

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

1910 or an instance of a mapped 

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

1912 classes. 

1913 

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

1915 object. 

1916 

1917 .. seealso:: 

1918 

1919 :ref:`session_partitioning` 

1920 

1921 :paramref:`.Session.binds` 

1922 

1923 :meth:`.Session.bind_table` 

1924 

1925 

1926 """ 

1927 self._add_bind(mapper, bind) 

1928 

1929 def bind_table(self, table, bind): 

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

1931 :class:`_engine.Engine` 

1932 or :class:`_engine.Connection`. 

1933 

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

1935 :meth:`.Session.get_bind` method. 

1936 

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

1938 which is typically the target 

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

1940 mapped. 

1941 

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

1943 object. 

1944 

1945 .. seealso:: 

1946 

1947 :ref:`session_partitioning` 

1948 

1949 :paramref:`.Session.binds` 

1950 

1951 :meth:`.Session.bind_mapper` 

1952 

1953 

1954 """ 

1955 self._add_bind(table, bind) 

1956 

1957 def get_bind( 

1958 self, 

1959 mapper=None, 

1960 clause=None, 

1961 bind=None, 

1962 _sa_skip_events=None, 

1963 _sa_skip_for_implicit_returning=False, 

1964 ): 

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

1966 

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

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

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

1970 

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

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

1973 appropriate bind to return. 

1974 

1975 Note that the "mapper" argument is usually present 

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

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

1978 individual INSERT/UPDATE/DELETE operation within a 

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

1980 

1981 The order of resolution is: 

1982 

1983 1. if mapper given and :paramref:`.Session.binds` is present, 

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

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

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

1987 superclasses to more general. 

1988 2. if clause given and ``Session.binds`` is present, 

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

1990 found in the given clause present in ``Session.binds``. 

1991 3. if ``Session.binds`` is present, return that. 

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

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

1994 associated with the clause. 

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

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

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

1998 selectable to which the mapper is mapped. 

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

2000 is raised. 

2001 

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

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

2004 of bind resolution scheme. See the example at 

2005 :ref:`session_custom_partitioning`. 

2006 

2007 :param mapper: 

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

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

2010 :class:`_orm.Mapper` 

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

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

2013 :class:`_schema.MetaData` 

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

2015 :class:`_orm.Mapper` 

2016 is mapped for a bind. 

2017 

2018 :param clause: 

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

2020 :func:`_expression.select`, 

2021 :func:`_expression.text`, 

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

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

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

2025 associated with 

2026 bound :class:`_schema.MetaData`. 

2027 

2028 .. seealso:: 

2029 

2030 :ref:`session_partitioning` 

2031 

2032 :paramref:`.Session.binds` 

2033 

2034 :meth:`.Session.bind_mapper` 

2035 

2036 :meth:`.Session.bind_table` 

2037 

2038 """ 

2039 

2040 # this function is documented as a subclassing hook, so we have 

2041 # to call this method even if the return is simple 

2042 if bind: 

2043 return bind 

2044 elif not self.__binds and self.bind: 

2045 # simplest and most common case, we have a bind and no 

2046 # per-mapper/table binds, we're done 

2047 return self.bind 

2048 

2049 # we don't have self.bind and either have self.__binds 

2050 # or we don't have self.__binds (which is legacy). Look at the 

2051 # mapper and the clause 

2052 if mapper is clause is None: 

2053 if self.bind: 

2054 return self.bind 

2055 else: 

2056 raise sa_exc.UnboundExecutionError( 

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

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

2059 "a binding." 

2060 ) 

2061 

2062 # look more closely at the mapper. 

2063 if mapper is not None: 

2064 try: 

2065 mapper = inspect(mapper) 

2066 except sa_exc.NoInspectionAvailable as err: 

2067 if isinstance(mapper, type): 

2068 util.raise_( 

2069 exc.UnmappedClassError(mapper), 

2070 replace_context=err, 

2071 ) 

2072 else: 

2073 raise 

2074 

2075 # match up the mapper or clause in the __binds 

2076 if self.__binds: 

2077 # matching mappers and selectables to entries in the 

2078 # binds dictionary; supported use case. 

2079 if mapper: 

2080 for cls in mapper.class_.__mro__: 

2081 if cls in self.__binds: 

2082 return self.__binds[cls] 

2083 if clause is None: 

2084 clause = mapper.persist_selectable 

2085 

2086 if clause is not None: 

2087 plugin_subject = clause._propagate_attrs.get( 

2088 "plugin_subject", None 

2089 ) 

2090 

2091 if plugin_subject is not None: 

2092 for cls in plugin_subject.mapper.class_.__mro__: 

2093 if cls in self.__binds: 

2094 return self.__binds[cls] 

2095 

2096 for obj in visitors.iterate(clause): 

2097 if obj in self.__binds: 

2098 return self.__binds[obj] 

2099 

2100 # none of the __binds matched, but we have a fallback bind. 

2101 # return that 

2102 if self.bind: 

2103 return self.bind 

2104 

2105 # now we are in legacy territory. looking for "bind" on tables 

2106 # that are via bound metadata. this goes away in 2.0. 

2107 

2108 future_msg = "" 

2109 future_code = "" 

2110 

2111 if mapper and clause is None: 

2112 clause = mapper.persist_selectable 

2113 

2114 if clause is not None: 

2115 if clause.bind: 

2116 if self.future: 

2117 future_msg = ( 

2118 " A bind was located via legacy bound metadata, but " 

2119 "since future=True is set on this Session, this " 

2120 "bind is ignored." 

2121 ) 

2122 else: 

2123 util.warn_deprecated_20( 

2124 "This Session located a target engine via bound " 

2125 "metadata; as this functionality will be removed in " 

2126 "SQLAlchemy 2.0, an Engine object should be passed " 

2127 "to the Session() constructor directly." 

2128 ) 

2129 return clause.bind 

2130 

2131 if mapper: 

2132 if mapper.persist_selectable.bind: 

2133 if self.future: 

2134 future_msg = ( 

2135 " A bind was located via legacy bound metadata, but " 

2136 "since future=True is set on this Session, this " 

2137 "bind is ignored." 

2138 ) 

2139 else: 

2140 util.warn_deprecated_20( 

2141 "This Session located a target engine via bound " 

2142 "metadata; as this functionality will be removed in " 

2143 "SQLAlchemy 2.0, an Engine object should be passed " 

2144 "to the Session() constructor directly." 

2145 ) 

2146 return mapper.persist_selectable.bind 

2147 

2148 context = [] 

2149 if mapper is not None: 

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

2151 if clause is not None: 

2152 context.append("SQL expression") 

2153 

2154 raise sa_exc.UnboundExecutionError( 

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

2156 % (", ".join(context), future_msg), 

2157 code=future_code, 

2158 ) 

2159 

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

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

2162 :class:`_orm.Session`. 

2163 

2164 """ 

2165 

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

2167 

2168 def _identity_lookup( 

2169 self, 

2170 mapper, 

2171 primary_key_identity, 

2172 identity_token=None, 

2173 passive=attributes.PASSIVE_OFF, 

2174 lazy_loaded_from=None, 

2175 ): 

2176 """Locate an object in the identity map. 

2177 

2178 Given a primary key identity, constructs an identity key and then 

2179 looks in the session's identity map. If present, the object may 

2180 be run through unexpiration rules (e.g. load unloaded attributes, 

2181 check if was deleted). 

2182 

2183 e.g.:: 

2184 

2185 obj = session._identity_lookup(inspect(SomeClass), (1, )) 

2186 

2187 :param mapper: mapper in use 

2188 :param primary_key_identity: the primary key we are searching for, as 

2189 a tuple. 

2190 :param identity_token: identity token that should be used to create 

2191 the identity key. Used as is, however overriding subclasses can 

2192 repurpose this in order to interpret the value in a special way, 

2193 such as if None then look among multiple target tokens. 

2194 :param passive: passive load flag passed to 

2195 :func:`.loading.get_from_identity`, which impacts the behavior if 

2196 the object is found; the object may be validated and/or unexpired 

2197 if the flag allows for SQL to be emitted. 

2198 :param lazy_loaded_from: an :class:`.InstanceState` that is 

2199 specifically asking for this identity as a related identity. Used 

2200 for sharding schemes where there is a correspondence between an object 

2201 and a related object being lazy-loaded (or otherwise 

2202 relationship-loaded). 

2203 

2204 :return: None if the object is not found in the identity map, *or* 

2205 if the object was unexpired and found to have been deleted. 

2206 if passive flags disallow SQL and the object is expired, returns 

2207 PASSIVE_NO_RESULT. In all other cases the instance is returned. 

2208 

2209 .. versionchanged:: 1.4.0 - the :meth:`.Session._identity_lookup` 

2210 method was moved from :class:`_query.Query` to 

2211 :class:`.Session`, to avoid having to instantiate the 

2212 :class:`_query.Query` object. 

2213 

2214 

2215 """ 

2216 

2217 key = mapper.identity_key_from_primary_key( 

2218 primary_key_identity, identity_token=identity_token 

2219 ) 

2220 return loading.get_from_identity(self, mapper, key, passive) 

2221 

2222 @property 

2223 @util.contextmanager 

2224 def no_autoflush(self): 

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

2226 

2227 e.g.:: 

2228 

2229 with session.no_autoflush: 

2230 

2231 some_object = SomeClass() 

2232 session.add(some_object) 

2233 # won't autoflush 

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

2235 

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

2237 will not be subject to flushes occurring upon query 

2238 access. This is useful when initializing a series 

2239 of objects which involve existing database queries, 

2240 where the uncompleted object should not yet be flushed. 

2241 

2242 """ 

2243 autoflush = self.autoflush 

2244 self.autoflush = False 

2245 try: 

2246 yield self 

2247 finally: 

2248 self.autoflush = autoflush 

2249 

2250 def _autoflush(self): 

2251 if self.autoflush and not self._flushing: 

2252 try: 

2253 self.flush() 

2254 except sa_exc.StatementError as e: 

2255 # note we are reraising StatementError as opposed to 

2256 # raising FlushError with "chaining" to remain compatible 

2257 # with code that catches StatementError, IntegrityError, 

2258 # etc. 

2259 e.add_detail( 

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

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

2262 "flush is occurring prematurely" 

2263 ) 

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

2265 

2266 def refresh(self, instance, attribute_names=None, with_for_update=None): 

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

2268 

2269 The selected attributes will first be expired as they would when using 

2270 :meth:`_orm.Session.expire`; then a SELECT statement will be issued to 

2271 the database to refresh column-oriented attributes with the current 

2272 value available in the current transaction. 

2273 

2274 :func:`_orm.relationship` oriented attributes will also be immediately 

2275 loaded if they were already eagerly loaded on the object, using the 

2276 same eager loading strategy that they were loaded with originally. 

2277 Unloaded relationship attributes will remain unloaded, as will 

2278 relationship attributes that were originally lazy loaded. 

2279 

2280 .. versionadded:: 1.4 - the :meth:`_orm.Session.refresh` method 

2281 can also refresh eagerly loaded attributes. 

2282 

2283 .. tip:: 

2284 

2285 While the :meth:`_orm.Session.refresh` method is capable of 

2286 refreshing both column and relationship oriented attributes, its 

2287 primary focus is on refreshing of local column-oriented attributes 

2288 on a single instance. For more open ended "refresh" functionality, 

2289 including the ability to refresh the attributes on many objects at 

2290 once while having explicit control over relationship loader 

2291 strategies, use the 

2292 :ref:`populate existing <orm_queryguide_populate_existing>` feature 

2293 instead. 

2294 

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

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

2297 in database state outside of that transaction. Refreshing 

2298 attributes usually only makes sense at the start of a transaction 

2299 where database rows have not yet been accessed. 

2300 

2301 :param attribute_names: optional. An iterable collection of 

2302 string attribute names indicating a subset of attributes to 

2303 be refreshed. 

2304 

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

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

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

2308 flags should match the parameters of 

2309 :meth:`_query.Query.with_for_update`. 

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

2311 

2312 .. seealso:: 

2313 

2314 :ref:`session_expire` - introductory material 

2315 

2316 :meth:`.Session.expire` 

2317 

2318 :meth:`.Session.expire_all` 

2319 

2320 :ref:`orm_queryguide_populate_existing` - allows any ORM query 

2321 to refresh objects as they would be loaded normally. 

2322 

2323 """ 

2324 try: 

2325 state = attributes.instance_state(instance) 

2326 except exc.NO_STATE as err: 

2327 util.raise_( 

2328 exc.UnmappedInstanceError(instance), 

2329 replace_context=err, 

2330 ) 

2331 

2332 self._expire_state(state, attribute_names) 

2333 

2334 if with_for_update == {}: 

2335 raise sa_exc.ArgumentError( 

2336 "with_for_update should be the boolean value " 

2337 "True, or a dictionary with options. " 

2338 "A blank dictionary is ambiguous." 

2339 ) 

2340 

2341 with_for_update = query.ForUpdateArg._from_argument(with_for_update) 

2342 

2343 stmt = sql.select(object_mapper(instance)) 

2344 if ( 

2345 loading.load_on_ident( 

2346 self, 

2347 stmt, 

2348 state.key, 

2349 refresh_state=state, 

2350 with_for_update=with_for_update, 

2351 only_load_props=attribute_names, 

2352 ) 

2353 is None 

2354 ): 

2355 raise sa_exc.InvalidRequestError( 

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

2357 ) 

2358 

2359 def expire_all(self): 

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

2361 

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

2363 a query will be issued using the 

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

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

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

2367 previously read in that same transaction, regardless of changes 

2368 in database state outside of that transaction. 

2369 

2370 To expire individual objects and individual attributes 

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

2372 

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

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

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

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

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

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

2379 

2380 .. seealso:: 

2381 

2382 :ref:`session_expire` - introductory material 

2383 

2384 :meth:`.Session.expire` 

2385 

2386 :meth:`.Session.refresh` 

2387 

2388 :meth:`_orm.Query.populate_existing` 

2389 

2390 """ 

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

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

2393 

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

2395 """Expire the attributes on an instance. 

2396 

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

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

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

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

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

2402 previously read in that same transaction, regardless of changes 

2403 in database state outside of that transaction. 

2404 

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

2406 use :meth:`Session.expire_all`. 

2407 

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

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

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

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

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

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

2414 transaction. 

2415 

2416 :param instance: The instance to be refreshed. 

2417 :param attribute_names: optional list of string attribute names 

2418 indicating a subset of attributes to be expired. 

2419 

2420 .. seealso:: 

2421 

2422 :ref:`session_expire` - introductory material 

2423 

2424 :meth:`.Session.expire` 

2425 

2426 :meth:`.Session.refresh` 

2427 

2428 :meth:`_orm.Query.populate_existing` 

2429 

2430 """ 

2431 try: 

2432 state = attributes.instance_state(instance) 

2433 except exc.NO_STATE as err: 

2434 util.raise_( 

2435 exc.UnmappedInstanceError(instance), 

2436 replace_context=err, 

2437 ) 

2438 self._expire_state(state, attribute_names) 

2439 

2440 def _expire_state(self, state, attribute_names): 

2441 self._validate_persistent(state) 

2442 if attribute_names: 

2443 state._expire_attributes(state.dict, attribute_names) 

2444 else: 

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

2446 # remove associations 

2447 cascaded = list( 

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

2449 ) 

2450 self._conditional_expire(state) 

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

2452 self._conditional_expire(st_) 

2453 

2454 def _conditional_expire(self, state, autoflush=None): 

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

2456 

2457 if state.key: 

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

2459 elif state in self._new: 

2460 self._new.pop(state) 

2461 state._detach(self) 

2462 

2463 def expunge(self, instance): 

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

2465 

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

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

2468 

2469 """ 

2470 try: 

2471 state = attributes.instance_state(instance) 

2472 except exc.NO_STATE as err: 

2473 util.raise_( 

2474 exc.UnmappedInstanceError(instance), 

2475 replace_context=err, 

2476 ) 

2477 if state.session_id is not self.hash_key: 

2478 raise sa_exc.InvalidRequestError( 

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

2480 ) 

2481 

2482 cascaded = list( 

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

2484 ) 

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

2486 

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

2488 for state in states: 

2489 if state in self._new: 

2490 self._new.pop(state) 

2491 elif self.identity_map.contains_state(state): 

2492 self.identity_map.safe_discard(state) 

2493 self._deleted.pop(state, None) 

2494 elif self._transaction: 

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

2496 # in the transaction snapshot 

2497 self._transaction._deleted.pop(state, None) 

2498 statelib.InstanceState._detach_states( 

2499 states, self, to_transient=to_transient 

2500 ) 

2501 

2502 def _register_persistent(self, states): 

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

2504 

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

2506 state as well as already persistent objects. 

2507 

2508 """ 

2509 

2510 pending_to_persistent = self.dispatch.pending_to_persistent or None 

2511 for state in states: 

2512 mapper = _state_mapper(state) 

2513 

2514 # prevent against last minute dereferences of the object 

2515 obj = state.obj() 

2516 if obj is not None: 

2517 

2518 instance_key = mapper._identity_key_from_state(state) 

2519 

2520 if ( 

2521 _none_set.intersection(instance_key[1]) 

2522 and not mapper.allow_partial_pks 

2523 or _none_set.issuperset(instance_key[1]) 

2524 ): 

2525 raise exc.FlushError( 

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

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

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

2529 "that the mapped Column object is configured to " 

2530 "expect these generated values. Ensure also that " 

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

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

2533 % state_str(state) 

2534 ) 

2535 

2536 if state.key is None: 

2537 state.key = instance_key 

2538 elif state.key != instance_key: 

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

2540 # state has already replaced this one in the identity 

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

2542 self.identity_map.safe_discard(state) 

2543 if state in self._transaction._key_switches: 

2544 orig_key = self._transaction._key_switches[state][0] 

2545 else: 

2546 orig_key = state.key 

2547 self._transaction._key_switches[state] = ( 

2548 orig_key, 

2549 instance_key, 

2550 ) 

2551 state.key = instance_key 

2552 

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

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

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

2556 old = self.identity_map.replace(state) 

2557 if ( 

2558 old is not None 

2559 and mapper._identity_key_from_state(old) == instance_key 

2560 and old.obj() is not None 

2561 ): 

2562 util.warn( 

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

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

2565 "load operations occurring inside of an event handler " 

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

2567 ) 

2568 state._orphaned_outside_of_session = False 

2569 

2570 statelib.InstanceState._commit_all_states( 

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

2572 ) 

2573 

2574 self._register_altered(states) 

2575 

2576 if pending_to_persistent is not None: 

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

2578 pending_to_persistent(self, state) 

2579 

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

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

2582 self._new.pop(state) 

2583 

2584 def _register_altered(self, states): 

2585 if self._transaction: 

2586 for state in states: 

2587 if state in self._new: 

2588 self._transaction._new[state] = True 

2589 else: 

2590 self._transaction._dirty[state] = True 

2591 

2592 def _remove_newly_deleted(self, states): 

2593 persistent_to_deleted = self.dispatch.persistent_to_deleted or None 

2594 for state in states: 

2595 if self._transaction: 

2596 self._transaction._deleted[state] = True 

2597 

2598 if persistent_to_deleted is not None: 

2599 # get a strong reference before we pop out of 

2600 # self._deleted 

2601 obj = state.obj() # noqa 

2602 

2603 self.identity_map.safe_discard(state) 

2604 self._deleted.pop(state, None) 

2605 state._deleted = True 

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

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

2608 # tracked as part of that 

2609 if persistent_to_deleted is not None: 

2610 persistent_to_deleted(self, state) 

2611 

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

2613 """Place an object into this :class:`_orm.Session`. 

2614 

2615 Objects that are in the :term:`transient` state when passed to the 

2616 :meth:`_orm.Session.add` method will move to the 

2617 :term:`pending` state, until the next flush, at which point they 

2618 will move to the :term:`persistent` state. 

2619 

2620 Objects that are in the :term:`detached` state when passed to the 

2621 :meth:`_orm.Session.add` method will move to the :term:`persistent` 

2622 state directly. 

2623 

2624 If the transaction used by the :class:`_orm.Session` is rolled back, 

2625 objects which were transient when they were passed to 

2626 :meth:`_orm.Session.add` will be moved back to the 

2627 :term:`transient` state, and will no longer be present within this 

2628 :class:`_orm.Session`. 

2629 

2630 .. seealso:: 

2631 

2632 :meth:`_orm.Session.add_all` 

2633 

2634 :ref:`session_adding` - at :ref:`session_basics` 

2635 

2636 """ 

2637 if _warn and self._warn_on_events: 

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

2639 

2640 try: 

2641 state = attributes.instance_state(instance) 

2642 except exc.NO_STATE as err: 

2643 util.raise_( 

2644 exc.UnmappedInstanceError(instance), 

2645 replace_context=err, 

2646 ) 

2647 

2648 self._save_or_update_state(state) 

2649 

2650 def add_all(self, instances): 

2651 """Add the given collection of instances to this :class:`_orm.Session`. 

2652 

2653 See the documentation for :meth:`_orm.Session.add` for a general 

2654 behavioral description. 

2655 

2656 .. seealso:: 

2657 

2658 :meth:`_orm.Session.add` 

2659 

2660 :ref:`session_adding` - at :ref:`session_basics` 

2661 

2662 """ 

2663 

2664 if self._warn_on_events: 

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

2666 

2667 for instance in instances: 

2668 self.add(instance, _warn=False) 

2669 

2670 def _save_or_update_state(self, state): 

2671 state._orphaned_outside_of_session = False 

2672 self._save_or_update_impl(state) 

2673 

2674 mapper = _state_mapper(state) 

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

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

2677 ): 

2678 self._save_or_update_impl(st_) 

2679 

2680 def delete(self, instance): 

2681 """Mark an instance as deleted. 

2682 

2683 The object is assumed to be either :term:`persistent` or 

2684 :term:`detached` when passed; after the method is called, the 

2685 object will remain in the :term:`persistent` state until the next 

2686 flush proceeds. During this time, the object will also be a member 

2687 of the :attr:`_orm.Session.deleted` collection. 

2688 

2689 When the next flush proceeds, the object will move to the 

2690 :term:`deleted` state, indicating a ``DELETE`` statement was emitted 

2691 for its row within the current transaction. When the transaction 

2692 is successfully committed, 

2693 the deleted object is moved to the :term:`detached` state and is 

2694 no longer present within this :class:`_orm.Session`. 

2695 

2696 .. seealso:: 

2697 

2698 :ref:`session_deleting` - at :ref:`session_basics` 

2699 

2700 """ 

2701 if self._warn_on_events: 

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

2703 

2704 try: 

2705 state = attributes.instance_state(instance) 

2706 except exc.NO_STATE as err: 

2707 util.raise_( 

2708 exc.UnmappedInstanceError(instance), 

2709 replace_context=err, 

2710 ) 

2711 

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

2713 

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

2715 

2716 if state.key is None: 

2717 if head: 

2718 raise sa_exc.InvalidRequestError( 

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

2720 ) 

2721 else: 

2722 return 

2723 

2724 to_attach = self._before_attach(state, obj) 

2725 

2726 if state in self._deleted: 

2727 return 

2728 

2729 self.identity_map.add(state) 

2730 

2731 if to_attach: 

2732 self._after_attach(state, obj) 

2733 

2734 if head: 

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

2736 # so that autoflush does not delete the item 

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

2738 cascade_states = list( 

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

2740 ) 

2741 

2742 self._deleted[state] = obj 

2743 

2744 if head: 

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

2746 self._delete_impl(st_, o, False) 

2747 

2748 def get( 

2749 self, 

2750 entity, 

2751 ident, 

2752 options=None, 

2753 populate_existing=False, 

2754 with_for_update=None, 

2755 identity_token=None, 

2756 execution_options=None, 

2757 ): 

2758 """Return an instance based on the given primary key identifier, 

2759 or ``None`` if not found. 

2760 

2761 E.g.:: 

2762 

2763 my_user = session.get(User, 5) 

2764 

2765 some_object = session.get(VersionedFoo, (5, 10)) 

2766 

2767 some_object = session.get( 

2768 VersionedFoo, 

2769 {"id": 5, "version_id": 10} 

2770 ) 

2771 

2772 .. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved 

2773 from the now deprecated :meth:`_orm.Query.get` method. 

2774 

2775 :meth:`_orm.Session.get` is special in that it provides direct 

2776 access to the identity map of the :class:`.Session`. 

2777 If the given primary key identifier is present 

2778 in the local identity map, the object is returned 

2779 directly from this collection and no SQL is emitted, 

2780 unless the object has been marked fully expired. 

2781 If not present, 

2782 a SELECT is performed in order to locate the object. 

2783 

2784 :meth:`_orm.Session.get` also will perform a check if 

2785 the object is present in the identity map and 

2786 marked as expired - a SELECT 

2787 is emitted to refresh the object as well as to 

2788 ensure that the row is still present. 

2789 If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

2790 

2791 :param entity: a mapped class or :class:`.Mapper` indicating the 

2792 type of entity to be loaded. 

2793 

2794 :param ident: A scalar, tuple, or dictionary representing the 

2795 primary key. For a composite (e.g. multiple column) primary key, 

2796 a tuple or dictionary should be passed. 

2797 

2798 For a single-column primary key, the scalar calling form is typically 

2799 the most expedient. If the primary key of a row is the value "5", 

2800 the call looks like:: 

2801 

2802 my_object = session.get(SomeClass, 5) 

2803 

2804 The tuple form contains primary key values typically in 

2805 the order in which they correspond to the mapped 

2806 :class:`_schema.Table` 

2807 object's primary key columns, or if the 

2808 :paramref:`_orm.Mapper.primary_key` configuration parameter were 

2809 used, in 

2810 the order used for that parameter. For example, if the primary key 

2811 of a row is represented by the integer 

2812 digits "5, 10" the call would look like:: 

2813 

2814 my_object = session.get(SomeClass, (5, 10)) 

2815 

2816 The dictionary form should include as keys the mapped attribute names 

2817 corresponding to each element of the primary key. If the mapped class 

2818 has the attributes ``id``, ``version_id`` as the attributes which 

2819 store the object's primary key value, the call would look like:: 

2820 

2821 my_object = session.get(SomeClass, {"id": 5, "version_id": 10}) 

2822 

2823 :param options: optional sequence of loader options which will be 

2824 applied to the query, if one is emitted. 

2825 

2826 :param populate_existing: causes the method to unconditionally emit 

2827 a SQL query and refresh the object with the newly loaded data, 

2828 regardless of whether or not the object is already present. 

2829 

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

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

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

2833 flags should match the parameters of 

2834 :meth:`_query.Query.with_for_update`. 

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

2836 

2837 :param execution_options: optional dictionary of execution options, 

2838 which will be associated with the query execution if one is emitted. 

2839 This dictionary can provide a subset of the options that are 

2840 accepted by :meth:`_engine.Connection.execution_options`, and may 

2841 also provide additional options understood only in an ORM context. 

2842 

2843 .. versionadded:: 1.4.29 

2844 

2845 .. seealso:: 

2846 

2847 :ref:`orm_queryguide_execution_options` - ORM-specific execution 

2848 options 

2849 

2850 :return: The object instance, or ``None``. 

2851 

2852 """ 

2853 return self._get_impl( 

2854 entity, 

2855 ident, 

2856 loading.load_on_pk_identity, 

2857 options, 

2858 populate_existing=populate_existing, 

2859 with_for_update=with_for_update, 

2860 identity_token=identity_token, 

2861 execution_options=execution_options, 

2862 ) 

2863 

2864 def _get_impl( 

2865 self, 

2866 entity, 

2867 primary_key_identity, 

2868 db_load_fn, 

2869 options=None, 

2870 populate_existing=False, 

2871 with_for_update=None, 

2872 identity_token=None, 

2873 execution_options=None, 

2874 ): 

2875 

2876 # convert composite types to individual args 

2877 if hasattr(primary_key_identity, "__composite_values__"): 

2878 primary_key_identity = primary_key_identity.__composite_values__() 

2879 

2880 mapper = inspect(entity) 

2881 

2882 if not mapper or not mapper.is_mapper: 

2883 raise sa_exc.ArgumentError( 

2884 "Expected mapped class or mapper, got: %r" % entity 

2885 ) 

2886 

2887 is_dict = isinstance(primary_key_identity, dict) 

2888 if not is_dict: 

2889 primary_key_identity = util.to_list( 

2890 primary_key_identity, default=(None,) 

2891 ) 

2892 

2893 if len(primary_key_identity) != len(mapper.primary_key): 

2894 raise sa_exc.InvalidRequestError( 

2895 "Incorrect number of values in identifier to formulate " 

2896 "primary key for session.get(); primary key columns " 

2897 "are %s" % ",".join("'%s'" % c for c in mapper.primary_key) 

2898 ) 

2899 

2900 if is_dict: 

2901 

2902 pk_synonyms = mapper._pk_synonyms 

2903 

2904 if pk_synonyms: 

2905 correct_keys = set(pk_synonyms).intersection( 

2906 primary_key_identity 

2907 ) 

2908 

2909 if correct_keys: 

2910 primary_key_identity = dict(primary_key_identity) 

2911 for k in correct_keys: 

2912 primary_key_identity[ 

2913 pk_synonyms[k] 

2914 ] = primary_key_identity[k] 

2915 

2916 try: 

2917 primary_key_identity = list( 

2918 primary_key_identity[prop.key] 

2919 for prop in mapper._identity_key_props 

2920 ) 

2921 

2922 except KeyError as err: 

2923 util.raise_( 

2924 sa_exc.InvalidRequestError( 

2925 "Incorrect names of values in identifier to formulate " 

2926 "primary key for session.get(); primary key attribute " 

2927 "names are %s (synonym names are also accepted)" 

2928 % ",".join( 

2929 "'%s'" % prop.key 

2930 for prop in mapper._identity_key_props 

2931 ) 

2932 ), 

2933 replace_context=err, 

2934 ) 

2935 

2936 if ( 

2937 not populate_existing 

2938 and not mapper.always_refresh 

2939 and with_for_update is None 

2940 ): 

2941 

2942 instance = self._identity_lookup( 

2943 mapper, primary_key_identity, identity_token=identity_token 

2944 ) 

2945 

2946 if instance is not None: 

2947 # reject calls for id in identity map but class 

2948 # mismatch. 

2949 if not issubclass(instance.__class__, mapper.class_): 

2950 return None 

2951 return instance 

2952 elif instance is attributes.PASSIVE_CLASS_MISMATCH: 

2953 return None 

2954 

2955 # set_label_style() not strictly necessary, however this will ensure 

2956 # that tablename_colname style is used which at the moment is 

2957 # asserted in a lot of unit tests :) 

2958 

2959 load_options = context.QueryContext.default_load_options 

2960 

2961 if populate_existing: 

2962 load_options += {"_populate_existing": populate_existing} 

2963 statement = sql.select(mapper).set_label_style( 

2964 LABEL_STYLE_TABLENAME_PLUS_COL 

2965 ) 

2966 if with_for_update is not None: 

2967 statement._for_update_arg = query.ForUpdateArg._from_argument( 

2968 with_for_update 

2969 ) 

2970 

2971 if options: 

2972 statement = statement.options(*options) 

2973 if execution_options: 

2974 statement = statement.execution_options(**execution_options) 

2975 return db_load_fn( 

2976 self, 

2977 statement, 

2978 primary_key_identity, 

2979 load_options=load_options, 

2980 ) 

2981 

2982 def merge(self, instance, load=True, options=None): 

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

2984 within this :class:`.Session`. 

2985 

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

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

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

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

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

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

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

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

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

2995 

2996 This operation cascades to associated instances if the association is 

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

2998 

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

3000 

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

3002 pending objects with overlapping primary keys in the same way 

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

3004 

3005 :param instance: Instance to be merged. 

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

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

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

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

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

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

3012 without re-querying the database. 

3013 

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

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

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

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

3018 the merge operation populates local attributes and 

3019 cascades to related objects and 

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

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

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

3023 any existing related objects or collections that might not 

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

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

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

3027 method. 

3028 :param options: optional sequence of loader options which will be 

3029 applied to the :meth:`_orm.Session.get` method when the merge 

3030 operation loads the existing version of the object from the database. 

3031 

3032 .. versionadded:: 1.4.24 

3033 

3034 

3035 .. seealso:: 

3036 

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

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

3039 

3040 """ 

3041 

3042 if self._warn_on_events: 

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

3044 

3045 _recursive = {} 

3046 _resolve_conflict_map = {} 

3047 

3048 if load: 

3049 # flush current contents if we expect to load data 

3050 self._autoflush() 

3051 

3052 object_mapper(instance) # verify mapped 

3053 autoflush = self.autoflush 

3054 try: 

3055 self.autoflush = False 

3056 return self._merge( 

3057 attributes.instance_state(instance), 

3058 attributes.instance_dict(instance), 

3059 load=load, 

3060 options=options, 

3061 _recursive=_recursive, 

3062 _resolve_conflict_map=_resolve_conflict_map, 

3063 ) 

3064 finally: 

3065 self.autoflush = autoflush 

3066 

3067 def _merge( 

3068 self, 

3069 state, 

3070 state_dict, 

3071 load=True, 

3072 options=None, 

3073 _recursive=None, 

3074 _resolve_conflict_map=None, 

3075 ): 

3076 mapper = _state_mapper(state) 

3077 if state in _recursive: 

3078 return _recursive[state] 

3079 

3080 new_instance = False 

3081 key = state.key 

3082 

3083 if key is None: 

3084 if state in self._new: 

3085 util.warn( 

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

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

3088 "to do" % state_str(state) 

3089 ) 

3090 

3091 if not load: 

3092 raise sa_exc.InvalidRequestError( 

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

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

3095 "all changes on mapped instances before merging with " 

3096 "load=False." 

3097 ) 

3098 key = mapper._identity_key_from_state(state) 

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

3100 not _none_set.intersection(key[1]) 

3101 or ( 

3102 mapper.allow_partial_pks 

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

3104 ) 

3105 ) 

3106 else: 

3107 key_is_persistent = True 

3108 

3109 if key in self.identity_map: 

3110 try: 

3111 merged = self.identity_map[key] 

3112 except KeyError: 

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

3114 merged = None 

3115 else: 

3116 merged = None 

3117 

3118 if merged is None: 

3119 if key_is_persistent and key in _resolve_conflict_map: 

3120 merged = _resolve_conflict_map[key] 

3121 

3122 elif not load: 

3123 if state.modified: 

3124 raise sa_exc.InvalidRequestError( 

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

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

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

3128 ) 

3129 merged = mapper.class_manager.new_instance() 

3130 merged_state = attributes.instance_state(merged) 

3131 merged_state.key = key 

3132 self._update_impl(merged_state) 

3133 new_instance = True 

3134 

3135 elif key_is_persistent: 

3136 merged = self.get( 

3137 mapper.class_, 

3138 key[1], 

3139 identity_token=key[2], 

3140 options=options, 

3141 ) 

3142 

3143 if merged is None: 

3144 merged = mapper.class_manager.new_instance() 

3145 merged_state = attributes.instance_state(merged) 

3146 merged_dict = attributes.instance_dict(merged) 

3147 new_instance = True 

3148 self._save_or_update_state(merged_state) 

3149 else: 

3150 merged_state = attributes.instance_state(merged) 

3151 merged_dict = attributes.instance_dict(merged) 

3152 

3153 _recursive[state] = merged 

3154 _resolve_conflict_map[key] = merged 

3155 

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

3157 # state out. 

3158 if state is not merged_state: 

3159 # version check if applicable 

3160 if mapper.version_id_col is not None: 

3161 existing_version = mapper._get_state_attr_by_column( 

3162 state, 

3163 state_dict, 

3164 mapper.version_id_col, 

3165 passive=attributes.PASSIVE_NO_INITIALIZE, 

3166 ) 

3167 

3168 merged_version = mapper._get_state_attr_by_column( 

3169 merged_state, 

3170 merged_dict, 

3171 mapper.version_id_col, 

3172 passive=attributes.PASSIVE_NO_INITIALIZE, 

3173 ) 

3174 

3175 if ( 

3176 existing_version is not attributes.PASSIVE_NO_RESULT 

3177 and merged_version is not attributes.PASSIVE_NO_RESULT 

3178 and existing_version != merged_version 

3179 ): 

3180 raise exc.StaleDataError( 

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

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

3183 "Leave the version attribute unset when " 

3184 "merging to update the most recent version." 

3185 % ( 

3186 existing_version, 

3187 state_str(merged_state), 

3188 merged_version, 

3189 ) 

3190 ) 

3191 

3192 merged_state.load_path = state.load_path 

3193 merged_state.load_options = state.load_options 

3194 

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

3196 # the callables_ that would have been generated by those 

3197 # load_options. 

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

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

3200 merged_state._copy_callables(state) 

3201 

3202 for prop in mapper.iterate_properties: 

3203 prop.merge( 

3204 self, 

3205 state, 

3206 state_dict, 

3207 merged_state, 

3208 merged_dict, 

3209 load, 

3210 _recursive, 

3211 _resolve_conflict_map, 

3212 ) 

3213 

3214 if not load: 

3215 # remove any history 

3216 merged_state._commit_all(merged_dict, self.identity_map) 

3217 merged_state.manager.dispatch._sa_event_merge_wo_load( 

3218 merged_state, None 

3219 ) 

3220 

3221 if new_instance: 

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

3223 return merged 

3224 

3225 def _validate_persistent(self, state): 

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

3227 raise sa_exc.InvalidRequestError( 

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

3229 % state_str(state) 

3230 ) 

3231 

3232 def _save_impl(self, state): 

3233 if state.key is not None: 

3234 raise sa_exc.InvalidRequestError( 

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

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

3237 ) 

3238 

3239 obj = state.obj() 

3240 to_attach = self._before_attach(state, obj) 

3241 if state not in self._new: 

3242 self._new[state] = obj 

3243 state.insert_order = len(self._new) 

3244 if to_attach: 

3245 self._after_attach(state, obj) 

3246 

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

3248 if state.key is None: 

3249 raise sa_exc.InvalidRequestError( 

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

3251 ) 

3252 

3253 if state._deleted: 

3254 if revert_deletion: 

3255 if not state._attached: 

3256 return 

3257 del state._deleted 

3258 else: 

3259 raise sa_exc.InvalidRequestError( 

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

3261 "Use the make_transient() " 

3262 "function to send this object back " 

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

3264 ) 

3265 

3266 obj = state.obj() 

3267 

3268 # check for late gc 

3269 if obj is None: 

3270 return 

3271 

3272 to_attach = self._before_attach(state, obj) 

3273 

3274 self._deleted.pop(state, None) 

3275 if revert_deletion: 

3276 self.identity_map.replace(state) 

3277 else: 

3278 self.identity_map.add(state) 

3279 

3280 if to_attach: 

3281 self._after_attach(state, obj) 

3282 elif revert_deletion: 

3283 self.dispatch.deleted_to_persistent(self, state) 

3284 

3285 def _save_or_update_impl(self, state): 

3286 if state.key is None: 

3287 self._save_impl(state) 

3288 else: 

3289 self._update_impl(state) 

3290 

3291 def enable_relationship_loading(self, obj): 

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

3293 object loading. 

3294 

3295 .. warning:: 

3296 

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

3298 use cases and is not recommended for general use. 

3299 

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

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

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

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

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

3305 will be unavailable. 

3306 

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

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

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

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

3311 

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

3313 Altering a relationship-bound attribute on the target object 

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

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

3316 

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

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

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

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

3321 related items. 

3322 

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

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

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

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

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

3328 

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

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

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

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

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

3334 

3335 .. seealso:: 

3336 

3337 :paramref:`_orm.relationship.load_on_pending` - this flag 

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

3339 are pending. 

3340 

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

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

3343 will unexpire attributes on access. 

3344 

3345 """ 

3346 try: 

3347 state = attributes.instance_state(obj) 

3348 except exc.NO_STATE as err: 

3349 util.raise_( 

3350 exc.UnmappedInstanceError(obj), 

3351 replace_context=err, 

3352 ) 

3353 

3354 to_attach = self._before_attach(state, obj) 

3355 state._load_pending = True 

3356 if to_attach: 

3357 self._after_attach(state, obj) 

3358 

3359 def _before_attach(self, state, obj): 

3360 self._autobegin() 

3361 

3362 if state.session_id == self.hash_key: 

3363 return False 

3364 

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

3366 raise sa_exc.InvalidRequestError( 

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

3368 "(this is '%s')" 

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

3370 ) 

3371 

3372 self.dispatch.before_attach(self, state) 

3373 

3374 return True 

3375 

3376 def _after_attach(self, state, obj): 

3377 state.session_id = self.hash_key 

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

3379 state._strong_obj = obj 

3380 self.dispatch.after_attach(self, state) 

3381 

3382 if state.key: 

3383 self.dispatch.detached_to_persistent(self, state) 

3384 else: 

3385 self.dispatch.transient_to_pending(self, state) 

3386 

3387 def __contains__(self, instance): 

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

3389 

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

3391 result of True. 

3392 

3393 """ 

3394 try: 

3395 state = attributes.instance_state(instance) 

3396 except exc.NO_STATE as err: 

3397 util.raise_( 

3398 exc.UnmappedInstanceError(instance), 

3399 replace_context=err, 

3400 ) 

3401 return self._contains_state(state) 

3402 

3403 def __iter__(self): 

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

3405 Session. 

3406 

3407 """ 

3408 return iter( 

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

3410 ) 

3411 

3412 def _contains_state(self, state): 

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

3414 

3415 def flush(self, objects=None): 

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

3417 

3418 Writes out all pending object creations, deletions and modifications 

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

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

3421 solver. 

3422 

3423 Database operations will be issued in the current transactional 

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

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

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

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

3428 

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

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

3431 operations into the flush. 

3432 

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

3434 only on elements that are in the given collection. 

3435 

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

3437 particular objects may need to be operated upon before the 

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

3439 

3440 """ 

3441 

3442 if self._flushing: 

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

3444 

3445 if self._is_clean(): 

3446 return 

3447 try: 

3448 self._flushing = True 

3449 self._flush(objects) 

3450 finally: 

3451 self._flushing = False 

3452 

3453 def _flush_warning(self, method): 

3454 util.warn( 

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

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

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

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

3459 ) 

3460 

3461 def _is_clean(self): 

3462 return ( 

3463 not self.identity_map.check_modified() 

3464 and not self._deleted 

3465 and not self._new 

3466 ) 

3467 

3468 def _flush(self, objects=None): 

3469 

3470 dirty = self._dirty_states 

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

3472 self.identity_map._modified.clear() 

3473 return 

3474 

3475 flush_context = UOWTransaction(self) 

3476 

3477 if self.dispatch.before_flush: 

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

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

3480 # added 

3481 dirty = self._dirty_states 

3482 

3483 deleted = set(self._deleted) 

3484 new = set(self._new) 

3485 

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

3487 

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

3489 if objects: 

3490 # specific list passed in 

3491 objset = set() 

3492 for o in objects: 

3493 try: 

3494 state = attributes.instance_state(o) 

3495 

3496 except exc.NO_STATE as err: 

3497 util.raise_( 

3498 exc.UnmappedInstanceError(o), 

3499 replace_context=err, 

3500 ) 

3501 objset.add(state) 

3502 else: 

3503 objset = None 

3504 

3505 # store objects whose fate has been decided 

3506 processed = set() 

3507 

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

3509 # orphans and throw them into deleted. 

3510 if objset: 

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

3512 else: 

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

3514 

3515 for state in proc: 

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

3517 

3518 is_persistent_orphan = is_orphan and state.has_identity 

3519 

3520 if ( 

3521 is_orphan 

3522 and not is_persistent_orphan 

3523 and state._orphaned_outside_of_session 

3524 ): 

3525 self._expunge_states([state]) 

3526 else: 

3527 _reg = flush_context.register_object( 

3528 state, isdelete=is_persistent_orphan 

3529 ) 

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

3531 processed.add(state) 

3532 

3533 # put all remaining deletes into the flush context. 

3534 if objset: 

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

3536 else: 

3537 proc = deleted.difference(processed) 

3538 for state in proc: 

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

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

3541 

3542 if not flush_context.has_work: 

3543 return 

3544 

3545 flush_context.transaction = transaction = self.begin(_subtrans=True) 

3546 try: 

3547 self._warn_on_events = True 

3548 try: 

3549 flush_context.execute() 

3550 finally: 

3551 self._warn_on_events = False 

3552 

3553 self.dispatch.after_flush(self, flush_context) 

3554 

3555 flush_context.finalize_flush_changes() 

3556 

3557 if not objects and self.identity_map._modified: 

3558 len_ = len(self.identity_map._modified) 

3559 

3560 statelib.InstanceState._commit_all_states( 

3561 [ 

3562 (state, state.dict) 

3563 for state in self.identity_map._modified 

3564 ], 

3565 instance_dict=self.identity_map, 

3566 ) 

3567 util.warn( 

3568 "Attribute history events accumulated on %d " 

3569 "previously clean instances " 

3570 "within inner-flush event handlers have been " 

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

3572 "Consider using set_committed_value() within " 

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

3574 ) 

3575 

3576 # useful assertions: 

3577 # if not objects: 

3578 # assert not self.identity_map._modified 

3579 # else: 

3580 # assert self.identity_map._modified == \ 

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

3582 

3583 self.dispatch.after_flush_postexec(self, flush_context) 

3584 

3585 transaction.commit() 

3586 

3587 except: 

3588 with util.safe_reraise(): 

3589 transaction.rollback(_capture_exception=True) 

3590 

3591 def bulk_save_objects( 

3592 self, 

3593 objects, 

3594 return_defaults=False, 

3595 update_changed_only=True, 

3596 preserve_order=True, 

3597 ): 

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

3599 

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

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

3602 grouped together into higher performing "executemany" 

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

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

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

3606 SQL expressions. 

3607 

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

3609 state is established on them. If the 

3610 :paramref:`_orm.Session.bulk_save_objects.return_defaults` flag is set, 

3611 then server-generated primary key values will be assigned to the 

3612 returned objects, but **not server side defaults**; this is a 

3613 limitation in the implementation. If stateful objects are desired, 

3614 please use the standard :meth:`_orm.Session.add_all` approach or 

3615 as an alternative newer mass-insert features such as 

3616 :ref:`orm_dml_returning_objects`. 

3617 

3618 .. legacy:: 

3619 

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

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

3622 Features such as object management, relationship handling, 

3623 and SQL clause support are silently omitted in favor of raw 

3624 INSERT/UPDATES of records. 

3625 

3626 In SQLAlchemy 2.0, improved versions of the bulk insert/update 

3627 methods are introduced, with clearer behavior and 

3628 documentation, new capabilities, and much better performance. 

3629 

3630 For 1.4 use, **please read the list of caveats at** 

3631 :ref:`bulk_operations_caveats` **before using this method, and 

3632 fully test and confirm the functionality of all code developed 

3633 using these systems.** 

3634 

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

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

3637 :class:`.Session` afterwards. 

3638 

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

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

3641 in traditional operation; if the object has the 

3642 :attr:`.InstanceState.key` 

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

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

3645 

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

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

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

3649 attributes present within each object are applied to the UPDATE 

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

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

3652 overhead of checking history on attributes. 

3653 

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

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

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

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

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

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

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

3661 reduces the performance gains** of the method overall. It is strongly 

3662 advised to please use the standard :meth:`_orm.Session.add_all` 

3663 approach. 

3664 

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

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

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

3668 with the exception of primary key attributes. 

3669 

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

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

3672 False, common types of objects are grouped into inserts 

3673 and updates, to allow for more batching opportunities. 

3674 

3675 .. versionadded:: 1.3 

3676 

3677 .. seealso:: 

3678 

3679 :ref:`bulk_operations` 

3680 

3681 :meth:`.Session.bulk_insert_mappings` 

3682 

3683 :meth:`.Session.bulk_update_mappings` 

3684 

3685 """ 

3686 

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

3688 

3689 if not preserve_order: 

3690 # the purpose of this sort is just so that common mappers 

3691 # and persistence states are grouped together, so that groupby 

3692 # will return a single group for a particular type of mapper. 

3693 # it's not trying to be deterministic beyond that. 

3694 obj_states = sorted( 

3695 obj_states, 

3696 key=lambda state: (id(state.mapper), state.key is not None), 

3697 ) 

3698 

3699 def grouping_key(state): 

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

3701 

3702 for (mapper, isupdate), states in itertools.groupby( 

3703 obj_states, grouping_key 

3704 ): 

3705 self._bulk_save_mappings( 

3706 mapper, 

3707 states, 

3708 isupdate, 

3709 True, 

3710 return_defaults, 

3711 update_changed_only, 

3712 False, 

3713 ) 

3714 

3715 def bulk_insert_mappings( 

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

3717 ): 

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

3719 

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

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

3722 grouped together into higher performing "executemany" 

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

3724 state management features in use, reducing latency when inserting 

3725 large numbers of simple rows. 

3726 

3727 The values within the dictionaries as given are typically passed 

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

3729 after 

3730 organizing the values within them across the tables to which 

3731 the given mapper is mapped. 

3732 

3733 .. versionadded:: 1.0.0 

3734 

3735 .. legacy:: 

3736 

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

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

3739 Features such as object management, relationship handling, 

3740 and SQL clause support are silently omitted in favor of raw 

3741 INSERT of records. 

3742 

3743 In SQLAlchemy 2.0, improved versions of the bulk insert/update 

3744 methods are introduced, with clearer behavior and 

3745 documentation, new capabilities, and much better performance. 

3746 

3747 For 1.4 use, **please read the list of caveats at** 

3748 :ref:`bulk_operations_caveats` **before using this method, and 

3749 fully test and confirm the functionality of all code developed 

3750 using these systems.** 

3751 

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

3753 object, 

3754 representing the single kind of object represented within the mapping 

3755 list. 

3756 

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

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

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

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

3761 keys to be populated into all tables. 

3762 

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

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

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

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

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

3768 to provide primary 

3769 key values ahead of time; however, 

3770 :paramref:`.Session.bulk_insert_mappings.return_defaults` 

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

3772 If the rows 

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

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

3775 is not used. 

3776 

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

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

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

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

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

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

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

3784 the rendered INSERT statement, which means it must be emitted as a 

3785 separate statement. By passing this flag, the full set of rows 

3786 are guaranteed to be batchable into one batch; the cost however is 

3787 that server-side defaults which are invoked by an omitted column will 

3788 be skipped, so care must be taken to ensure that these are not 

3789 necessary. 

3790 

3791 .. warning:: 

3792 

3793 When this flag is set, **server side default SQL values will 

3794 not be invoked** for those columns that are inserted as NULL; 

3795 the NULL value will be sent explicitly. Care must be taken 

3796 to ensure that no server-side default functions need to be 

3797 invoked for the operation as a whole. 

3798 

3799 .. versionadded:: 1.1 

3800 

3801 .. seealso:: 

3802 

3803 :ref:`bulk_operations` 

3804 

3805 :meth:`.Session.bulk_save_objects` 

3806 

3807 :meth:`.Session.bulk_update_mappings` 

3808 

3809 """ 

3810 self._bulk_save_mappings( 

3811 mapper, 

3812 mappings, 

3813 False, 

3814 False, 

3815 return_defaults, 

3816 False, 

3817 render_nulls, 

3818 ) 

3819 

3820 def bulk_update_mappings(self, mapper, mappings): 

3821 """Perform a bulk update of the given list of mapping dictionaries. 

3822 

3823 The bulk update feature allows plain Python dictionaries to be used as 

3824 the source of simple UPDATE operations which can be more easily 

3825 grouped together into higher performing "executemany" 

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

3827 state management features in use, reducing latency when updating 

3828 large numbers of simple rows. 

3829 

3830 .. versionadded:: 1.0.0 

3831 

3832 .. legacy:: 

3833 

3834 The bulk update feature allows for a lower-latency UPDATE 

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

3836 Features such as object management, relationship handling, 

3837 and SQL clause support are silently omitted in favor of raw 

3838 UPDATES of records. 

3839 

3840 In SQLAlchemy 2.0, improved versions of the bulk insert/update 

3841 methods are introduced, with clearer behavior and 

3842 documentation, new capabilities, and much better performance. 

3843 

3844 For 1.4 use, **please read the list of caveats at** 

3845 :ref:`bulk_operations_caveats` **before using this method, and 

3846 fully test and confirm the functionality of all code developed 

3847 using these systems.** 

3848 

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

3850 object, 

3851 representing the single kind of object represented within the mapping 

3852 list. 

3853 

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

3855 state of the mapped row to be updated, in terms of the attribute names 

3856 on the mapped class. If the mapping refers to multiple tables, such 

3857 as a joined-inheritance mapping, each dictionary may contain keys 

3858 corresponding to all tables. All those keys which are present and 

3859 are not part of the primary key are applied to the SET clause of the 

3860 UPDATE statement; the primary key values, which are required, are 

3861 applied to the WHERE clause. 

3862 

3863 

3864 .. seealso:: 

3865 

3866 :ref:`bulk_operations` 

3867 

3868 :meth:`.Session.bulk_insert_mappings` 

3869 

3870 :meth:`.Session.bulk_save_objects` 

3871 

3872 """ 

3873 self._bulk_save_mappings( 

3874 mapper, mappings, True, False, False, False, False 

3875 ) 

3876 

3877 def _bulk_save_mappings( 

3878 self, 

3879 mapper, 

3880 mappings, 

3881 isupdate, 

3882 isstates, 

3883 return_defaults, 

3884 update_changed_only, 

3885 render_nulls, 

3886 ): 

3887 mapper = _class_to_mapper(mapper) 

3888 self._flushing = True 

3889 

3890 transaction = self.begin(_subtrans=True) 

3891 try: 

3892 if isupdate: 

3893 persistence._bulk_update( 

3894 mapper, 

3895 mappings, 

3896 transaction, 

3897 isstates, 

3898 update_changed_only, 

3899 ) 

3900 else: 

3901 persistence._bulk_insert( 

3902 mapper, 

3903 mappings, 

3904 transaction, 

3905 isstates, 

3906 return_defaults, 

3907 render_nulls, 

3908 ) 

3909 transaction.commit() 

3910 

3911 except: 

3912 with util.safe_reraise(): 

3913 transaction.rollback(_capture_exception=True) 

3914 finally: 

3915 self._flushing = False 

3916 

3917 def is_modified(self, instance, include_collections=True): 

3918 r"""Return ``True`` if the given instance has locally 

3919 modified attributes. 

3920 

3921 This method retrieves the history for each instrumented 

3922 attribute on the instance and performs a comparison of the current 

3923 value to its previously committed value, if any. 

3924 

3925 It is in effect a more expensive and accurate 

3926 version of checking for the given instance in the 

3927 :attr:`.Session.dirty` collection; a full test for 

3928 each attribute's net "dirty" status is performed. 

3929 

3930 E.g.:: 

3931 

3932 return session.is_modified(someobject) 

3933 

3934 A few caveats to this method apply: 

3935 

3936 * Instances present in the :attr:`.Session.dirty` collection may 

3937 report ``False`` when tested with this method. This is because 

3938 the object may have received change events via attribute mutation, 

3939 thus placing it in :attr:`.Session.dirty`, but ultimately the state 

3940 is the same as that loaded from the database, resulting in no net 

3941 change here. 

3942 * Scalar attributes may not have recorded the previously set 

3943 value when a new value was applied, if the attribute was not loaded, 

3944 or was expired, at the time the new value was received - in these 

3945 cases, the attribute is assumed to have a change, even if there is 

3946 ultimately no net change against its database value. SQLAlchemy in 

3947 most cases does not need the "old" value when a set event occurs, so 

3948 it skips the expense of a SQL call if the old value isn't present, 

3949 based on the assumption that an UPDATE of the scalar value is 

3950 usually needed, and in those few cases where it isn't, is less 

3951 expensive on average than issuing a defensive SELECT. 

3952 

3953 The "old" value is fetched unconditionally upon set only if the 

3954 attribute container has the ``active_history`` flag set to ``True``. 

3955 This flag is set typically for primary key attributes and scalar 

3956 object references that are not a simple many-to-one. To set this 

3957 flag for any arbitrary mapped column, use the ``active_history`` 

3958 argument with :func:`.column_property`. 

3959 

3960 :param instance: mapped instance to be tested for pending changes. 

3961 :param include_collections: Indicates if multivalued collections 

3962 should be included in the operation. Setting this to ``False`` is a 

3963 way to detect only local-column based properties (i.e. scalar columns 

3964 or many-to-one foreign keys) that would result in an UPDATE for this 

3965 instance upon flush. 

3966 

3967 """ 

3968 state = object_state(instance) 

3969 

3970 if not state.modified: 

3971 return False 

3972 

3973 dict_ = state.dict 

3974 

3975 for attr in state.manager.attributes: 

3976 if ( 

3977 not include_collections 

3978 and hasattr(attr.impl, "get_collection") 

3979 ) or not hasattr(attr.impl, "get_history"): 

3980 continue 

3981 

3982 (added, unchanged, deleted) = attr.impl.get_history( 

3983 state, dict_, passive=attributes.NO_CHANGE 

3984 ) 

3985 

3986 if added or deleted: 

3987 return True 

3988 else: 

3989 return False 

3990 

3991 @property 

3992 def is_active(self): 

3993 """True if this :class:`.Session` not in "partial rollback" state. 

3994 

3995 .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins 

3996 a new transaction immediately, so this attribute will be False 

3997 when the :class:`_orm.Session` is first instantiated. 

3998 

3999 "partial rollback" state typically indicates that the flush process 

4000 of the :class:`_orm.Session` has failed, and that the 

4001 :meth:`_orm.Session.rollback` method must be emitted in order to 

4002 fully roll back the transaction. 

4003 

4004 If this :class:`_orm.Session` is not in a transaction at all, the 

4005 :class:`_orm.Session` will autobegin when it is first used, so in this 

4006 case :attr:`_orm.Session.is_active` will return True. 

4007 

4008 Otherwise, if this :class:`_orm.Session` is within a transaction, 

4009 and that transaction has not been rolled back internally, the 

4010 :attr:`_orm.Session.is_active` will also return True. 

4011 

4012 .. seealso:: 

4013 

4014 :ref:`faq_session_rollback` 

4015 

4016 :meth:`_orm.Session.in_transaction` 

4017 

4018 """ 

4019 if self.autocommit: 

4020 return ( 

4021 self._transaction is not None and self._transaction.is_active 

4022 ) 

4023 else: 

4024 return self._transaction is None or self._transaction.is_active 

4025 

4026 identity_map = None 

4027 """A mapping of object identities to objects themselves. 

4028 

4029 Iterating through ``Session.identity_map.values()`` provides 

4030 access to the full set of persistent objects (i.e., those 

4031 that have row identity) currently in the session. 

4032 

4033 .. seealso:: 

4034 

4035 :func:`.identity_key` - helper function to produce the keys used 

4036 in this dictionary. 

4037 

4038 """ 

4039 

4040 @property 

4041 def _dirty_states(self): 

4042 """The set of all persistent states considered dirty. 

4043 

4044 This method returns all states that were modified including 

4045 those that were possibly deleted. 

4046 

4047 """ 

4048 return self.identity_map._dirty_states() 

4049 

4050 @property 

4051 def dirty(self): 

4052 """The set of all persistent instances considered dirty. 

4053 

4054 E.g.:: 

4055 

4056 some_mapped_object in session.dirty 

4057 

4058 Instances are considered dirty when they were modified but not 

4059 deleted. 

4060 

4061 Note that this 'dirty' calculation is 'optimistic'; most 

4062 attribute-setting or collection modification operations will 

4063 mark an instance as 'dirty' and place it in this set, even if 

4064 there is no net change to the attribute's value. At flush 

4065 time, the value of each attribute is compared to its 

4066 previously saved value, and if there's no net change, no SQL 

4067 operation will occur (this is a more expensive operation so 

4068 it's only done at flush time). 

4069 

4070 To check if an instance has actionable net changes to its 

4071 attributes, use the :meth:`.Session.is_modified` method. 

4072 

4073 """ 

4074 return util.IdentitySet( 

4075 [ 

4076 state.obj() 

4077 for state in self._dirty_states 

4078 if state not in self._deleted 

4079 ] 

4080 ) 

4081 

4082 @property 

4083 def deleted(self): 

4084 "The set of all instances marked as 'deleted' within this ``Session``" 

4085 

4086 return util.IdentitySet(list(self._deleted.values())) 

4087 

4088 @property 

4089 def new(self): 

4090 "The set of all instances marked as 'new' within this ``Session``." 

4091 

4092 return util.IdentitySet(list(self._new.values())) 

4093 

4094 

4095class sessionmaker(_SessionClassMethods): 

4096 """A configurable :class:`.Session` factory. 

4097 

4098 The :class:`.sessionmaker` factory generates new 

4099 :class:`.Session` objects when called, creating them given 

4100 the configurational arguments established here. 

4101 

4102 e.g.:: 

4103 

4104 from sqlalchemy import create_engine 

4105 from sqlalchemy.orm import sessionmaker 

4106 

4107 # an Engine, which the Session will use for connection 

4108 # resources 

4109 engine = create_engine('postgresql://scott:tiger@localhost/') 

4110 

4111 Session = sessionmaker(engine) 

4112 

4113 with Session() as session: 

4114 session.add(some_object) 

4115 session.add(some_other_object) 

4116 session.commit() 

4117 

4118 Context manager use is optional; otherwise, the returned 

4119 :class:`_orm.Session` object may be closed explicitly via the 

4120 :meth:`_orm.Session.close` method. Using a 

4121 ``try:/finally:`` block is optional, however will ensure that the close 

4122 takes place even if there are database errors:: 

4123 

4124 session = Session() 

4125 try: 

4126 session.add(some_object) 

4127 session.add(some_other_object) 

4128 session.commit() 

4129 finally: 

4130 session.close() 

4131 

4132 :class:`.sessionmaker` acts as a factory for :class:`_orm.Session` 

4133 objects in the same way as an :class:`_engine.Engine` acts as a factory 

4134 for :class:`_engine.Connection` objects. In this way it also includes 

4135 a :meth:`_orm.sessionmaker.begin` method, that provides a context 

4136 manager which both begins and commits a transaction, as well as closes 

4137 out the :class:`_orm.Session` when complete, rolling back the transaction 

4138 if any errors occur:: 

4139 

4140 Session = sessionmaker(engine) 

4141 

4142 with Session.begin() as session: 

4143 session.add(some_object) 

4144 session.add(some_other_object) 

4145 # commits transaction, closes session 

4146 

4147 .. versionadded:: 1.4 

4148 

4149 When calling upon :class:`_orm.sessionmaker` to construct a 

4150 :class:`_orm.Session`, keyword arguments may also be passed to the 

4151 method; these arguments will override that of the globally configured 

4152 parameters. Below we use a :class:`_orm.sessionmaker` bound to a certain 

4153 :class:`_engine.Engine` to produce a :class:`_orm.Session` that is instead 

4154 bound to a specific :class:`_engine.Connection` procured from that engine:: 

4155 

4156 Session = sessionmaker(engine) 

4157 

4158 # bind an individual session to a connection 

4159 

4160 with engine.connect() as connection: 

4161 with Session(bind=connection) as session: 

4162 # work with session 

4163 

4164 The class also includes a method :meth:`_orm.sessionmaker.configure`, which 

4165 can be used to specify additional keyword arguments to the factory, which 

4166 will take effect for subsequent :class:`.Session` objects generated. This 

4167 is usually used to associate one or more :class:`_engine.Engine` objects 

4168 with an existing 

4169 :class:`.sessionmaker` factory before it is first used:: 

4170 

4171 # application starts, sessionmaker does not have 

4172 # an engine bound yet 

4173 Session = sessionmaker() 

4174 

4175 # ... later, when an engine URL is read from a configuration 

4176 # file or other events allow the engine to be created 

4177 engine = create_engine('sqlite:///foo.db') 

4178 Session.configure(bind=engine) 

4179 

4180 sess = Session() 

4181 # work with session 

4182 

4183 .. seealso:: 

4184 

4185 :ref:`session_getting` - introductory text on creating 

4186 sessions using :class:`.sessionmaker`. 

4187 

4188 """ 

4189 

4190 def __init__( 

4191 self, 

4192 bind=None, 

4193 class_=Session, 

4194 autoflush=True, 

4195 autocommit=False, 

4196 expire_on_commit=True, 

4197 info=None, 

4198 **kw 

4199 ): 

4200 r"""Construct a new :class:`.sessionmaker`. 

4201 

4202 All arguments here except for ``class_`` correspond to arguments 

4203 accepted by :class:`.Session` directly. See the 

4204 :meth:`.Session.__init__` docstring for more details on parameters. 

4205 

4206 :param bind: a :class:`_engine.Engine` or other :class:`.Connectable` 

4207 with 

4208 which newly created :class:`.Session` objects will be associated. 

4209 :param class\_: class to use in order to create new :class:`.Session` 

4210 objects. Defaults to :class:`.Session`. 

4211 :param autoflush: The autoflush setting to use with newly created 

4212 :class:`.Session` objects. 

4213 :param autocommit: The autocommit setting to use with newly created 

4214 :class:`.Session` objects. 

4215 :param expire_on_commit=True: the 

4216 :paramref:`_orm.Session.expire_on_commit` setting to use 

4217 with newly created :class:`.Session` objects. 

4218 

4219 :param info: optional dictionary of information that will be available 

4220 via :attr:`.Session.info`. Note this dictionary is *updated*, not 

4221 replaced, when the ``info`` parameter is specified to the specific 

4222 :class:`.Session` construction operation. 

4223 

4224 :param \**kw: all other keyword arguments are passed to the 

4225 constructor of newly created :class:`.Session` objects. 

4226 

4227 """ 

4228 kw["bind"] = bind 

4229 kw["autoflush"] = autoflush 

4230 kw["autocommit"] = autocommit 

4231 kw["expire_on_commit"] = expire_on_commit 

4232 if info is not None: 

4233 kw["info"] = info 

4234 self.kw = kw 

4235 # make our own subclass of the given class, so that 

4236 # events can be associated with it specifically. 

4237 self.class_ = type(class_.__name__, (class_,), {}) 

4238 

4239 def begin(self): 

4240 """Produce a context manager that both provides a new 

4241 :class:`_orm.Session` as well as a transaction that commits. 

4242 

4243 

4244 e.g.:: 

4245 

4246 Session = sessionmaker(some_engine) 

4247 

4248 with Session.begin() as session: 

4249 session.add(some_object) 

4250 

4251 # commits transaction, closes session 

4252 

4253 .. versionadded:: 1.4 

4254 

4255 

4256 """ 

4257 

4258 session = self() 

4259 return session._maker_context_manager() 

4260 

4261 def __call__(self, **local_kw): 

4262 """Produce a new :class:`.Session` object using the configuration 

4263 established in this :class:`.sessionmaker`. 

4264 

4265 In Python, the ``__call__`` method is invoked on an object when 

4266 it is "called" in the same way as a function:: 

4267 

4268 Session = sessionmaker() 

4269 session = Session() # invokes sessionmaker.__call__() 

4270 

4271 """ 

4272 for k, v in self.kw.items(): 

4273 if k == "info" and "info" in local_kw: 

4274 d = v.copy() 

4275 d.update(local_kw["info"]) 

4276 local_kw["info"] = d 

4277 else: 

4278 local_kw.setdefault(k, v) 

4279 return self.class_(**local_kw) 

4280 

4281 def configure(self, **new_kw): 

4282 """(Re)configure the arguments for this sessionmaker. 

4283 

4284 e.g.:: 

4285 

4286 Session = sessionmaker() 

4287 

4288 Session.configure(bind=create_engine('sqlite://')) 

4289 """ 

4290 self.kw.update(new_kw) 

4291 

4292 def __repr__(self): 

4293 return "%s(class_=%r, %s)" % ( 

4294 self.__class__.__name__, 

4295 self.class_.__name__, 

4296 ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), 

4297 ) 

4298 

4299 

4300def close_all_sessions(): 

4301 """Close all sessions in memory. 

4302 

4303 This function consults a global registry of all :class:`.Session` objects 

4304 and calls :meth:`.Session.close` on them, which resets them to a clean 

4305 state. 

4306 

4307 This function is not for general use but may be useful for test suites 

4308 within the teardown scheme. 

4309 

4310 .. versionadded:: 1.3 

4311 

4312 """ 

4313 

4314 for sess in _sessions.values(): 

4315 sess.close() 

4316 

4317 

4318def make_transient(instance): 

4319 """Alter the state of the given instance so that it is :term:`transient`. 

4320 

4321 .. note:: 

4322 

4323 :func:`.make_transient` is a special-case function for 

4324 advanced use cases only. 

4325 

4326 The given mapped instance is assumed to be in the :term:`persistent` or 

4327 :term:`detached` state. The function will remove its association with any 

4328 :class:`.Session` as well as its :attr:`.InstanceState.identity`. The 

4329 effect is that the object will behave as though it were newly constructed, 

4330 except retaining any attribute / collection values that were loaded at the 

4331 time of the call. The :attr:`.InstanceState.deleted` flag is also reset 

4332 if this object had been deleted as a result of using 

4333 :meth:`.Session.delete`. 

4334 

4335 .. warning:: 

4336 

4337 :func:`.make_transient` does **not** "unexpire" or otherwise eagerly 

4338 load ORM-mapped attributes that are not currently loaded at the time 

4339 the function is called. This includes attributes which: 

4340 

4341 * were expired via :meth:`.Session.expire` 

4342 

4343 * were expired as the natural effect of committing a session 

4344 transaction, e.g. :meth:`.Session.commit` 

4345 

4346 * are normally :term:`lazy loaded` but are not currently loaded 

4347 

4348 * are "deferred" via :ref:`deferred` and are not yet loaded 

4349 

4350 * were not present in the query which loaded this object, such as that 

4351 which is common in joined table inheritance and other scenarios. 

4352 

4353 After :func:`.make_transient` is called, unloaded attributes such 

4354 as those above will normally resolve to the value ``None`` when 

4355 accessed, or an empty collection for a collection-oriented attribute. 

4356 As the object is transient and un-associated with any database 

4357 identity, it will no longer retrieve these values. 

4358 

4359 .. seealso:: 

4360 

4361 :func:`.make_transient_to_detached` 

4362 

4363 """ 

4364 state = attributes.instance_state(instance) 

4365 s = _state_session(state) 

4366 if s: 

4367 s._expunge_states([state]) 

4368 

4369 # remove expired state 

4370 state.expired_attributes.clear() 

4371 

4372 # remove deferred callables 

4373 if state.callables: 

4374 del state.callables 

4375 

4376 if state.key: 

4377 del state.key 

4378 if state._deleted: 

4379 del state._deleted 

4380 

4381 

4382def make_transient_to_detached(instance): 

4383 """Make the given transient instance :term:`detached`. 

4384 

4385 .. note:: 

4386 

4387 :func:`.make_transient_to_detached` is a special-case function for 

4388 advanced use cases only. 

4389 

4390 All attribute history on the given instance 

4391 will be reset as though the instance were freshly loaded 

4392 from a query. Missing attributes will be marked as expired. 

4393 The primary key attributes of the object, which are required, will be made 

4394 into the "key" of the instance. 

4395 

4396 The object can then be added to a session, or merged 

4397 possibly with the load=False flag, at which point it will look 

4398 as if it were loaded that way, without emitting SQL. 

4399 

4400 This is a special use case function that differs from a normal 

4401 call to :meth:`.Session.merge` in that a given persistent state 

4402 can be manufactured without any SQL calls. 

4403 

4404 .. seealso:: 

4405 

4406 :func:`.make_transient` 

4407 

4408 :meth:`.Session.enable_relationship_loading` 

4409 

4410 """ 

4411 state = attributes.instance_state(instance) 

4412 if state.session_id or state.key: 

4413 raise sa_exc.InvalidRequestError("Given object must be transient") 

4414 state.key = state.mapper._identity_key_from_state(state) 

4415 if state._deleted: 

4416 del state._deleted 

4417 state._commit_all(state.dict) 

4418 state._expire_attributes(state.dict, state.unloaded_expirable) 

4419 

4420 

4421def object_session(instance): 

4422 """Return the :class:`.Session` to which the given instance belongs. 

4423 

4424 This is essentially the same as the :attr:`.InstanceState.session` 

4425 accessor. See that attribute for details. 

4426 

4427 """ 

4428 

4429 try: 

4430 state = attributes.instance_state(instance) 

4431 except exc.NO_STATE as err: 

4432 util.raise_( 

4433 exc.UnmappedInstanceError(instance), 

4434 replace_context=err, 

4435 ) 

4436 else: 

4437 return _state_session(state) 

4438 

4439 

4440_new_sessionid = util.counter()