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

1198 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# orm/session.py 

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

3# <see AUTHORS file> 

4# 

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

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

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

8 

9 

10import itertools 

11import sys 

12import weakref 

13 

14from . import attributes 

15from . import context 

16from . import exc 

17from . import identity 

18from . import loading 

19from . import persistence 

20from . import query 

21from . import state as statelib 

22from .base import _class_to_mapper 

23from .base import _none_set 

24from .base import _state_mapper 

25from .base import instance_str 

26from .base import object_mapper 

27from .base import object_state 

28from .base import state_str 

29from .unitofwork import UOWTransaction 

30from .. import engine 

31from .. import exc as sa_exc 

32from .. import sql 

33from .. import util 

34from ..engine.util import TransactionalContext 

35from ..inspection import inspect 

36from ..sql import coercions 

37from ..sql import dml 

38from ..sql import roles 

39from ..sql import visitors 

40from ..sql.base import CompileState 

41from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

42 

43__all__ = [ 

44 "Session", 

45 "SessionTransaction", 

46 "sessionmaker", 

47 "ORMExecuteState", 

48 "close_all_sessions", 

49 "make_transient", 

50 "make_transient_to_detached", 

51 "object_session", 

52] 

53 

54_sessions = weakref.WeakValueDictionary() 

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

56""" 

57 

58statelib._sessions = _sessions 

59 

60 

61def _state_session(state): 

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

63 associated, if any. 

64 """ 

65 return state.session 

66 

67 

68class _SessionClassMethods(object): 

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

70 

71 @classmethod 

72 @util.deprecated( 

73 "1.3", 

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

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

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

77 ) 

78 def close_all(cls): 

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

80 

81 close_all_sessions() 

82 

83 @classmethod 

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

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

86 """Return an identity key. 

87 

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

89 

90 """ 

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

92 

93 @classmethod 

94 def object_session(cls, instance): 

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

96 

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

98 

99 """ 

100 

101 return object_session(instance) 

102 

103 

104ACTIVE = util.symbol("ACTIVE") 

105PREPARED = util.symbol("PREPARED") 

106COMMITTED = util.symbol("COMMITTED") 

107DEACTIVE = util.symbol("DEACTIVE") 

108CLOSED = util.symbol("CLOSED") 

109 

110 

111class ORMExecuteState(util.MemoizedSlots): 

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

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

114 

115 .. versionadded:: 1.4 

116 

117 .. seealso:: 

118 

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

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

121 

122 """ 

123 

124 __slots__ = ( 

125 "session", 

126 "statement", 

127 "parameters", 

128 "execution_options", 

129 "local_execution_options", 

130 "bind_arguments", 

131 "_compile_state_cls", 

132 "_starting_event_idx", 

133 "_events_todo", 

134 "_update_execution_options", 

135 ) 

136 

137 def __init__( 

138 self, 

139 session, 

140 statement, 

141 parameters, 

142 execution_options, 

143 bind_arguments, 

144 compile_state_cls, 

145 events_todo, 

146 ): 

147 self.session = session 

148 self.statement = statement 

149 self.parameters = parameters 

150 self.local_execution_options = execution_options 

151 self.execution_options = statement._execution_options.union( 

152 execution_options 

153 ) 

154 self.bind_arguments = bind_arguments 

155 self._compile_state_cls = compile_state_cls 

156 self._events_todo = list(events_todo) 

157 

158 def _remaining_events(self): 

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

160 

161 def invoke_statement( 

162 self, 

163 statement=None, 

164 params=None, 

165 execution_options=None, 

166 bind_arguments=None, 

167 ): 

168 """Execute the statement represented by this 

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

170 already proceeded. 

171 

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

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

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

175 that want to override how the ultimate 

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

177 retrieve results from an offline cache or which concatenate results 

178 from multiple executions. 

179 

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

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

182 is propagated to the calling 

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

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

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

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

187 

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

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

190 

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

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

193 :class:`.ORMExecuteState`. 

194 

195 :param execution_options: optional dictionary of execution options 

196 will be merged into the existing 

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

198 :class:`.ORMExecuteState`. 

199 

200 :param bind_arguments: optional dictionary of bind_arguments 

201 which will be merged amongst the current 

202 :attr:`.ORMExecuteState.bind_arguments` 

203 of this :class:`.ORMExecuteState`. 

204 

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

206 

207 .. seealso:: 

208 

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

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

211 

212 

213 """ 

214 

215 if statement is None: 

216 statement = self.statement 

217 

218 _bind_arguments = dict(self.bind_arguments) 

219 if bind_arguments: 

220 _bind_arguments.update(bind_arguments) 

221 _bind_arguments["_sa_skip_events"] = True 

222 

223 if params: 

224 _params = dict(self.parameters) 

225 _params.update(params) 

226 else: 

227 _params = self.parameters 

228 

229 _execution_options = self.local_execution_options 

230 if execution_options: 

231 _execution_options = _execution_options.union(execution_options) 

232 

233 return self.session.execute( 

234 statement, 

235 _params, 

236 _execution_options, 

237 _bind_arguments, 

238 _parent_execute_state=self, 

239 ) 

240 

241 @property 

242 def bind_mapper(self): 

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

244 

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

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

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

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

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

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

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

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

253 would be selected. 

254 

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

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

257 way of getting this mapper. 

258 

259 .. versionadded:: 1.4.0b2 

260 

261 .. seealso:: 

262 

263 :attr:`_orm.ORMExecuteState.all_mappers` 

264 

265 

266 """ 

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

268 

269 @property 

270 def all_mappers(self): 

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

272 involved at the top level of this statement. 

273 

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

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

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

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

278 

279 .. versionadded:: 1.4.0b2 

280 

281 .. seealso:: 

282 

283 :attr:`_orm.ORMExecuteState.bind_mapper` 

284 

285 

286 

287 """ 

288 if not self.is_orm_statement: 

289 return [] 

290 elif self.is_select: 

291 result = [] 

292 seen = set() 

293 for d in self.statement.column_descriptions: 

294 ent = d["entity"] 

295 if ent: 

296 insp = inspect(ent, raiseerr=False) 

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

298 seen.add(insp.mapper) 

299 result.append(insp.mapper) 

300 return result 

301 elif self.is_update or self.is_delete: 

302 return [self.bind_mapper] 

303 else: 

304 return [] 

305 

306 @property 

307 def is_orm_statement(self): 

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

309 

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

311 invoked contains ORM entities as subjects. For a statement 

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

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

314 and no ORM-level automation takes place. 

315 

316 """ 

317 return self._compile_state_cls is not None 

318 

319 @property 

320 def is_select(self): 

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

322 return self.statement.is_select 

323 

324 @property 

325 def is_insert(self): 

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

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

328 

329 @property 

330 def is_update(self): 

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

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

333 

334 @property 

335 def is_delete(self): 

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

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

338 

339 @property 

340 def _is_crud(self): 

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

342 

343 def update_execution_options(self, **opts): 

344 # TODO: no coverage 

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

346 

347 def _orm_compile_options(self): 

348 if not self.is_select: 

349 return None 

350 opts = self.statement._compile_options 

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

352 return opts 

353 else: 

354 return None 

355 

356 @property 

357 def lazy_loaded_from(self): 

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

359 for a lazy load operation. 

360 

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

362 sharding extension, where it is available within specific query 

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

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

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

366 compilation time. 

367 

368 """ 

369 return self.load_options._lazy_loaded_from 

370 

371 @property 

372 def loader_strategy_path(self): 

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

374 

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

376 when a particular object or collection is being loaded. 

377 

378 """ 

379 opts = self._orm_compile_options() 

380 if opts is not None: 

381 return opts._current_path 

382 else: 

383 return None 

384 

385 @property 

386 def is_column_load(self): 

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

388 attributes on an existing ORM object. 

389 

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

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

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

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

394 loaded. 

395 

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

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

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

399 and loader options travelling with the instance 

400 will have already been added to the query. 

401 

402 .. versionadded:: 1.4.0b2 

403 

404 .. seealso:: 

405 

406 :attr:`_orm.ORMExecuteState.is_relationship_load` 

407 

408 """ 

409 opts = self._orm_compile_options() 

410 return opts is not None and opts._for_refresh_state 

411 

412 @property 

413 def is_relationship_load(self): 

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

415 relationship. 

416 

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

418 SelectInLoader, SubqueryLoader, or similar, and the entire 

419 SELECT statement being emitted is on behalf of a relationship 

420 load. 

421 

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

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

424 capable of being propagated to relationship loaders and should 

425 be already present. 

426 

427 .. seealso:: 

428 

429 :attr:`_orm.ORMExecuteState.is_column_load` 

430 

431 """ 

432 opts = self._orm_compile_options() 

433 if opts is None: 

434 return False 

435 path = self.loader_strategy_path 

436 return path is not None and not path.is_root 

437 

438 @property 

439 def load_options(self): 

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

441 

442 if not self.is_select: 

443 raise sa_exc.InvalidRequestError( 

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

445 "so there are no load options." 

446 ) 

447 return self.execution_options.get( 

448 "_sa_orm_load_options", context.QueryContext.default_load_options 

449 ) 

450 

451 @property 

452 def update_delete_options(self): 

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

454 execution.""" 

455 

456 if not self._is_crud: 

457 raise sa_exc.InvalidRequestError( 

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

459 "statement so there are no update options." 

460 ) 

461 return self.execution_options.get( 

462 "_sa_orm_update_options", 

463 persistence.BulkUDCompileState.default_update_options, 

464 ) 

465 

466 @property 

467 def user_defined_options(self): 

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

469 associated with the statement being invoked. 

470 

471 """ 

472 return [ 

473 opt 

474 for opt in self.statement._with_options 

475 if not opt._is_compile_state and not opt._is_legacy_option 

476 ] 

477 

478 

479class SessionTransaction(TransactionalContext): 

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

481 

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

483 :meth:`_orm.Session.begin` 

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

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

486 transactions. 

487 

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

489 at: :ref:`unitofwork_transaction`. 

490 

491 

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

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

494 

495 .. seealso:: 

496 

497 :ref:`unitofwork_transaction` 

498 

499 :meth:`.Session.begin` 

500 

501 :meth:`.Session.begin_nested` 

502 

503 :meth:`.Session.rollback` 

504 

505 :meth:`.Session.commit` 

506 

507 :meth:`.Session.in_transaction` 

508 

509 :meth:`.Session.in_nested_transaction` 

510 

511 :meth:`.Session.get_transaction` 

512 

513 :meth:`.Session.get_nested_transaction` 

514 

515 

516 """ 

517 

518 _rollback_exception = None 

519 

520 def __init__( 

521 self, 

522 session, 

523 parent=None, 

524 nested=False, 

525 autobegin=False, 

526 ): 

527 TransactionalContext._trans_ctx_check(session) 

528 

529 self.session = session 

530 self._connections = {} 

531 self._parent = parent 

532 self.nested = nested 

533 if nested: 

534 self._previous_nested_transaction = session._nested_transaction 

535 self._state = ACTIVE 

536 if not parent and nested: 

537 raise sa_exc.InvalidRequestError( 

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

539 "transaction is in progress" 

540 ) 

541 

542 self._take_snapshot(autobegin=autobegin) 

543 

544 # make sure transaction is assigned before we call the 

545 # dispatch 

546 self.session._transaction = self 

547 

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

549 

550 @property 

551 def parent(self): 

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

553 :class:`.SessionTransaction`. 

554 

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

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

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

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

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

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

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

562 

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

564 

565 """ 

566 return self._parent 

567 

568 nested = False 

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

570 

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

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

573 

574 """ 

575 

576 @property 

577 def is_active(self): 

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

579 

580 def _assert_active( 

581 self, 

582 prepared_ok=False, 

583 rollback_ok=False, 

584 deactive_ok=False, 

585 closed_msg="This transaction is closed", 

586 ): 

587 if self._state is COMMITTED: 

588 raise sa_exc.InvalidRequestError( 

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

590 "SQL can be emitted within this transaction." 

591 ) 

592 elif self._state is PREPARED: 

593 if not prepared_ok: 

594 raise sa_exc.InvalidRequestError( 

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

596 "SQL can be emitted within this transaction." 

597 ) 

598 elif self._state is DEACTIVE: 

599 if not deactive_ok and not rollback_ok: 

600 if self._rollback_exception: 

601 raise sa_exc.PendingRollbackError( 

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

603 "due to a previous exception during flush." 

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

605 "first issue Session.rollback()." 

606 " Original exception was: %s" 

607 % self._rollback_exception, 

608 code="7s2a", 

609 ) 

610 elif not deactive_ok: 

611 raise sa_exc.InvalidRequestError( 

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

613 "SQL transaction being rolled back; no further " 

614 "SQL can be emitted within this transaction." 

615 ) 

616 elif self._state is CLOSED: 

617 raise sa_exc.ResourceClosedError(closed_msg) 

618 

619 @property 

620 def _is_transaction_boundary(self): 

621 return self.nested or not self._parent 

622 

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

624 self._assert_active() 

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

626 return self._connection_for_bind(bind, execution_options) 

627 

628 def _begin(self, nested=False): 

629 self._assert_active() 

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

631 

632 def _iterate_self_and_parents(self, upto=None): 

633 

634 current = self 

635 result = () 

636 while current: 

637 result += (current,) 

638 if current._parent is upto: 

639 break 

640 elif current._parent is None: 

641 raise sa_exc.InvalidRequestError( 

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

643 % (upto) 

644 ) 

645 else: 

646 current = current._parent 

647 

648 return result 

649 

650 def _take_snapshot(self, autobegin=False): 

651 if not self._is_transaction_boundary: 

652 self._new = self._parent._new 

653 self._deleted = self._parent._deleted 

654 self._dirty = self._parent._dirty 

655 self._key_switches = self._parent._key_switches 

656 return 

657 

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

659 self.session.flush() 

660 

661 self._new = weakref.WeakKeyDictionary() 

662 self._deleted = weakref.WeakKeyDictionary() 

663 self._dirty = weakref.WeakKeyDictionary() 

664 self._key_switches = weakref.WeakKeyDictionary() 

665 

666 def _restore_snapshot(self, dirty_only=False): 

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

668 

669 Corresponds to a rollback. 

670 

671 """ 

672 assert self._is_transaction_boundary 

673 

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

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

676 

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

678 # we probably can do this conditionally based on 

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

680 self.session.identity_map.safe_discard(s) 

681 

682 # restore the old key 

683 s.key = oldkey 

684 

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

686 if s not in to_expunge: 

687 self.session.identity_map.replace(s) 

688 

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

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

691 

692 assert not self.session._deleted 

693 

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

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

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

697 

698 def _remove_snapshot(self): 

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

700 

701 Corresponds to a commit. 

702 

703 """ 

704 assert self._is_transaction_boundary 

705 

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

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

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

709 

710 statelib.InstanceState._detach_states( 

711 list(self._deleted), self.session 

712 ) 

713 self._deleted.clear() 

714 elif self.nested: 

715 self._parent._new.update(self._new) 

716 self._parent._dirty.update(self._dirty) 

717 self._parent._deleted.update(self._deleted) 

718 self._parent._key_switches.update(self._key_switches) 

719 

720 def _connection_for_bind(self, bind, execution_options): 

721 self._assert_active() 

722 

723 if bind in self._connections: 

724 if execution_options: 

725 util.warn( 

726 "Connection is already established for the " 

727 "given bind; execution_options ignored" 

728 ) 

729 return self._connections[bind][0] 

730 

731 local_connect = False 

732 should_commit = True 

733 

734 if self._parent: 

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

736 if not self.nested: 

737 return conn 

738 else: 

739 if isinstance(bind, engine.Connection): 

740 conn = bind 

741 if conn.engine in self._connections: 

742 raise sa_exc.InvalidRequestError( 

743 "Session already has a Connection associated for the " 

744 "given Connection's Engine" 

745 ) 

746 else: 

747 conn = bind.connect() 

748 local_connect = True 

749 

750 try: 

751 if execution_options: 

752 conn = conn.execution_options(**execution_options) 

753 

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

755 transaction = conn.begin_twophase() 

756 elif self.nested: 

757 transaction = conn.begin_nested() 

758 elif conn.in_transaction(): 

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

760 # commit that transaction unless it is a savepoint 

761 if conn.in_nested_transaction(): 

762 transaction = conn.get_nested_transaction() 

763 else: 

764 transaction = conn.get_transaction() 

765 should_commit = False 

766 else: 

767 transaction = conn.begin() 

768 except: 

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

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

771 if local_connect: 

772 conn.close() 

773 raise 

774 else: 

775 bind_is_connection = isinstance(bind, engine.Connection) 

776 

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

778 conn, 

779 transaction, 

780 should_commit, 

781 not bind_is_connection, 

782 ) 

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

784 return conn 

785 

786 def prepare(self): 

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

788 raise sa_exc.InvalidRequestError( 

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

790 "can't prepare." 

791 ) 

792 self._prepare_impl() 

793 

794 def _prepare_impl(self): 

795 self._assert_active() 

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

797 self.session.dispatch.before_commit(self.session) 

798 

799 stx = self.session._transaction 

800 if stx is not self: 

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

802 subtransaction.commit() 

803 

804 if not self.session._flushing: 

805 for _flush_guard in range(100): 

806 if self.session._is_clean(): 

807 break 

808 self.session.flush() 

809 else: 

810 raise exc.FlushError( 

811 "Over 100 subsequent flushes have occurred within " 

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

813 "creating new objects?" 

814 ) 

815 

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

817 try: 

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

819 t[1].prepare() 

820 except: 

821 with util.safe_reraise(): 

822 self.rollback() 

823 

824 self._state = PREPARED 

825 

826 def commit(self, _to_root=False): 

827 self._assert_active(prepared_ok=True) 

828 if self._state is not PREPARED: 

829 self._prepare_impl() 

830 

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

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

833 self._connections.values() 

834 ): 

835 if should_commit: 

836 trans.commit() 

837 

838 self._state = COMMITTED 

839 self.session.dispatch.after_commit(self.session) 

840 

841 self._remove_snapshot() 

842 

843 self.close() 

844 

845 if _to_root and self._parent: 

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

847 

848 return self._parent 

849 

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

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

852 

853 stx = self.session._transaction 

854 if stx is not self: 

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

856 subtransaction.close() 

857 

858 boundary = self 

859 rollback_err = None 

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

861 for transaction in self._iterate_self_and_parents(): 

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

863 try: 

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

865 t[1].rollback() 

866 

867 transaction._state = DEACTIVE 

868 self.session.dispatch.after_rollback(self.session) 

869 except: 

870 rollback_err = sys.exc_info() 

871 finally: 

872 transaction._state = DEACTIVE 

873 transaction._restore_snapshot( 

874 dirty_only=transaction.nested 

875 ) 

876 boundary = transaction 

877 break 

878 else: 

879 transaction._state = DEACTIVE 

880 

881 sess = self.session 

882 

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

884 

885 # if items were added, deleted, or mutated 

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

887 util.warn( 

888 "Session's state has been changed on " 

889 "a non-active transaction - this state " 

890 "will be discarded." 

891 ) 

892 boundary._restore_snapshot(dirty_only=boundary.nested) 

893 

894 self.close() 

895 

896 if self._parent and _capture_exception: 

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

898 

899 if rollback_err: 

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

901 

902 sess.dispatch.after_soft_rollback(sess, self) 

903 

904 if _to_root and self._parent: 

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

906 return self._parent 

907 

908 def close(self, invalidate=False): 

909 if self.nested: 

910 self.session._nested_transaction = ( 

911 self._previous_nested_transaction 

912 ) 

913 

914 self.session._transaction = self._parent 

915 

916 if self._parent is None: 

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

918 self._connections.values() 

919 ): 

920 if invalidate: 

921 connection.invalidate() 

922 if should_commit and transaction.is_active: 

923 transaction.close() 

924 if autoclose: 

925 connection.close() 

926 

927 self._state = CLOSED 

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

929 

930 self.session = None 

931 self._connections = None 

932 

933 def _get_subject(self): 

934 return self.session 

935 

936 def _transaction_is_active(self): 

937 return self._state is ACTIVE 

938 

939 def _transaction_is_closed(self): 

940 return self._state is CLOSED 

941 

942 def _rollback_can_be_called(self): 

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

944 

945 

946class Session(_SessionClassMethods): 

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

948 

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

950 

951 

952 """ 

953 

954 _is_asyncio = False 

955 

956 @util.deprecated_params( 

957 autocommit=( 

958 "2.0", 

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

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

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

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

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

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

965 ), 

966 ) 

967 def __init__( 

968 self, 

969 bind=None, 

970 autoflush=True, 

971 future=False, 

972 expire_on_commit=True, 

973 autocommit=False, 

974 twophase=False, 

975 binds=None, 

976 enable_baked_queries=True, 

977 info=None, 

978 query_cls=None, 

979 ): 

980 r"""Construct a new Session. 

981 

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

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

984 set of arguments. 

985 

986 :param autocommit: 

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

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

989 individual statement executions, will acquire connections from the 

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

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

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

993 When using this mode, the 

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

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

996 

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

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

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

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

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

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

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

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

1005 

1006 .. seealso:: 

1007 

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

1009 

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

1011 :class:`_engine.Connection` to 

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

1013 operations performed by this session will execute via this 

1014 connectable. 

1015 

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

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

1018 objects as the source of 

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

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

1021 arbitrary Python classes that are bases for mapped classes, 

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

1023 The 

1024 values of the dictionary are then instances of 

1025 :class:`_engine.Engine` 

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

1027 Operations which 

1028 proceed relative to a particular mapped class will consult this 

1029 dictionary for the closest matching entity in order to determine 

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

1031 operation. The complete heuristics for resolution are 

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

1033 

1034 Session = sessionmaker(binds={ 

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

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

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

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

1039 }) 

1040 

1041 .. seealso:: 

1042 

1043 :ref:`session_partitioning` 

1044 

1045 :meth:`.Session.bind_mapper` 

1046 

1047 :meth:`.Session.bind_table` 

1048 

1049 :meth:`.Session.get_bind` 

1050 

1051 

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

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

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

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

1056 constructor for ``Session``. 

1057 

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

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

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

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

1062 this particular extension is disabled. 

1063 

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

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

1066 flag therefore only affects applications that are making explicit 

1067 use of this extension within their own code. 

1068 

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

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

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

1072 transaction will load from the most recent database state. 

1073 

1074 .. seealso:: 

1075 

1076 :ref:`session_committing` 

1077 

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

1079 behavior. Future mode includes the following behaviors: 

1080 

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

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

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

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

1085 in use 

1086 

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

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

1089 set. 

1090 

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

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

1093 "False" behavior. 

1094 

1095 .. versionadded:: 1.4 

1096 

1097 .. seealso:: 

1098 

1099 :ref:`migration_20_toplevel` 

1100 

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

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

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

1104 construction time so that modifications to the per- 

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

1106 :class:`.Session`. 

1107 

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

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

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

1111 

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

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

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

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

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

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

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

1119 transaction, before each transaction is committed. 

1120 

1121 """ 

1122 self.identity_map = identity.WeakInstanceDict() 

1123 

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

1125 self._deleted = {} # same 

1126 self.bind = bind 

1127 self.__binds = {} 

1128 self._flushing = False 

1129 self._warn_on_events = False 

1130 self._transaction = None 

1131 self._nested_transaction = None 

1132 self.future = future 

1133 self.hash_key = _new_sessionid() 

1134 self.autoflush = autoflush 

1135 self.expire_on_commit = expire_on_commit 

1136 self.enable_baked_queries = enable_baked_queries 

1137 

1138 if autocommit: 

1139 if future: 

1140 raise sa_exc.ArgumentError( 

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

1142 ) 

1143 self.autocommit = True 

1144 else: 

1145 self.autocommit = False 

1146 

1147 self.twophase = twophase 

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

1149 if info: 

1150 self.info.update(info) 

1151 

1152 if binds is not None: 

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

1154 self._add_bind(key, bind) 

1155 

1156 _sessions[self.hash_key] = self 

1157 

1158 # used by sqlalchemy.engine.util.TransactionalContext 

1159 _trans_context_manager = None 

1160 

1161 connection_callable = None 

1162 

1163 def __enter__(self): 

1164 return self 

1165 

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

1167 self.close() 

1168 

1169 @util.contextmanager 

1170 def _maker_context_manager(self): 

1171 with self: 

1172 with self.begin(): 

1173 yield self 

1174 

1175 @property 

1176 @util.deprecated_20( 

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

1178 alternative="For context manager use, use " 

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

1180 "the current root transaction, use " 

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

1182 warn_on_attribute_access=True, 

1183 ) 

1184 def transaction(self): 

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

1186 

1187 May be None if no transaction has begun yet. 

1188 

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

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

1191 transaction has begun yet. 

1192 

1193 

1194 """ 

1195 return self._legacy_transaction() 

1196 

1197 def _legacy_transaction(self): 

1198 if not self.future: 

1199 self._autobegin() 

1200 return self._transaction 

1201 

1202 def in_transaction(self): 

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

1204 

1205 .. versionadded:: 1.4 

1206 

1207 .. seealso:: 

1208 

1209 :attr:`_orm.Session.is_active` 

1210 

1211 

1212 """ 

1213 return self._transaction is not None 

1214 

1215 def in_nested_transaction(self): 

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

1217 transaction, e.g. SAVEPOINT. 

1218 

1219 .. versionadded:: 1.4 

1220 

1221 """ 

1222 return self._nested_transaction is not None 

1223 

1224 def get_transaction(self): 

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

1226 

1227 .. versionadded:: 1.4 

1228 

1229 """ 

1230 trans = self._transaction 

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

1232 trans = trans._parent 

1233 return trans 

1234 

1235 def get_nested_transaction(self): 

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

1237 

1238 .. versionadded:: 1.4 

1239 

1240 """ 

1241 

1242 return self._nested_transaction 

1243 

1244 @util.memoized_property 

1245 def info(self): 

1246 """A user-modifiable dictionary. 

1247 

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

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

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

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

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

1253 

1254 """ 

1255 return {} 

1256 

1257 def _autobegin(self): 

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

1259 

1260 trans = SessionTransaction(self, autobegin=True) 

1261 assert self._transaction is trans 

1262 return True 

1263 

1264 return False 

1265 

1266 @util.deprecated_params( 

1267 subtransactions=( 

1268 "2.0", 

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

1270 "deprecated and " 

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

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

1273 "background on a compatible alternative pattern.", 

1274 ) 

1275 ) 

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

1277 """Begin a transaction, or nested transaction, 

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

1279 

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

1281 so that normally it is not necessary to call the 

1282 :meth:`_orm.Session.begin` 

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

1284 the scope of when the transactional state is begun. 

1285 

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

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

1288 

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

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

1291 documentation on SAVEPOINT transactions, please see 

1292 :ref:`session_begin_nested`. 

1293 

1294 :param subtransactions: if True, indicates that this 

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

1296 

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

1298 :class:`.SessionTransaction` 

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

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

1301 an example. 

1302 

1303 .. seealso:: 

1304 

1305 :ref:`session_autobegin` 

1306 

1307 :ref:`unitofwork_transaction` 

1308 

1309 :meth:`.Session.begin_nested` 

1310 

1311 

1312 """ 

1313 

1314 if subtransactions and self.future: 

1315 raise NotImplementedError( 

1316 "subtransactions are not implemented in future " 

1317 "Session objects." 

1318 ) 

1319 

1320 if self._autobegin(): 

1321 if not subtransactions and not nested and not _subtrans: 

1322 return self._transaction 

1323 

1324 if self._transaction is not None: 

1325 if subtransactions or _subtrans or nested: 

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

1327 assert self._transaction is trans 

1328 if nested: 

1329 self._nested_transaction = trans 

1330 else: 

1331 raise sa_exc.InvalidRequestError( 

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

1333 ) 

1334 elif not self.autocommit: 

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

1336 # a subtransaction 

1337 

1338 assert not nested and not _subtrans and not subtransactions 

1339 trans = SessionTransaction(self) 

1340 assert self._transaction is trans 

1341 else: 

1342 # legacy autocommit mode 

1343 assert not self.future 

1344 trans = SessionTransaction(self, nested=nested) 

1345 assert self._transaction is trans 

1346 

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

1348 

1349 def begin_nested(self): 

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

1351 

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

1353 SAVEPOINT for this method to function correctly. 

1354 

1355 For documentation on SAVEPOINT 

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

1357 

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

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

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

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

1362 

1363 .. seealso:: 

1364 

1365 :ref:`session_begin_nested` 

1366 

1367 :ref:`pysqlite_serializable` - special workarounds required 

1368 with the SQLite driver in order for SAVEPOINT to work 

1369 correctly. 

1370 

1371 """ 

1372 return self.begin(nested=True) 

1373 

1374 def rollback(self): 

1375 """Rollback the current transaction in progress. 

1376 

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

1378 

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

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

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

1382 

1383 When 

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

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

1386 the topmost database transaction, discarding any nested 

1387 transactions that may be in progress. 

1388 

1389 .. seealso:: 

1390 

1391 :ref:`session_rollback` 

1392 

1393 :ref:`unitofwork_transaction` 

1394 

1395 """ 

1396 if self._transaction is None: 

1397 pass 

1398 else: 

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

1400 

1401 def commit(self): 

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

1403 

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

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

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

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

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

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

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

1411 to disable this behavior. 

1412 

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

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

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

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

1417 normally affect the database unless pending flush changes were 

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

1419 rules. 

1420 

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

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

1423 the operation will release the current SAVEPOINT but not commit 

1424 the outermost database transaction. 

1425 

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

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

1428 transaction is committed unconditionally, automatically releasing any 

1429 SAVEPOINTs in effect. 

1430 

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

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

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

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

1435 rather than the actual database transaction, if a transaction 

1436 is in progress. 

1437 

1438 .. seealso:: 

1439 

1440 :ref:`session_committing` 

1441 

1442 :ref:`unitofwork_transaction` 

1443 

1444 :ref:`asyncio_orm_avoid_lazyloads` 

1445 

1446 """ 

1447 if self._transaction is None: 

1448 if not self._autobegin(): 

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

1450 

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

1452 

1453 def prepare(self): 

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

1455 

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

1457 :exc:`~sqlalchemy.exc.InvalidRequestError`. 

1458 

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

1460 current transaction is not such, an 

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

1462 

1463 """ 

1464 if self._transaction is None: 

1465 if not self._autobegin(): 

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

1467 

1468 self._transaction.prepare() 

1469 

1470 def connection( 

1471 self, 

1472 bind_arguments=None, 

1473 close_with_result=False, 

1474 execution_options=None, 

1475 **kw 

1476 ): 

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

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

1479 

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

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

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

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

1484 returned (note that no 

1485 transactional state is established with the DBAPI until the first 

1486 SQL statement is emitted). 

1487 

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

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

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

1491 :class:`_engine.Engine`. 

1492 

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

1494 resolved through any of the optional keyword arguments. This 

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

1496 

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

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

1499 to :meth:`.Session.get_bind`. 

1500 

1501 :param bind: 

1502 deprecated; use bind_arguments 

1503 

1504 :param mapper: 

1505 deprecated; use bind_arguments 

1506 

1507 :param clause: 

1508 deprecated; use bind_arguments 

1509 

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

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

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

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

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

1515 transaction in progress. 

1516 

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

1518 in SQLAlchemy 2.0 

1519 

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

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

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

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

1524 the arguments are ignored. 

1525 

1526 .. seealso:: 

1527 

1528 :ref:`session_transaction_isolation` 

1529 

1530 :param \**kw: 

1531 deprecated; use bind_arguments 

1532 

1533 """ 

1534 

1535 if not bind_arguments: 

1536 bind_arguments = kw 

1537 

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

1539 if bind is None: 

1540 bind = self.get_bind(**bind_arguments) 

1541 

1542 return self._connection_for_bind( 

1543 bind, 

1544 close_with_result=close_with_result, 

1545 execution_options=execution_options, 

1546 ) 

1547 

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

1549 TransactionalContext._trans_ctx_check(self) 

1550 

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

1552 return self._transaction._connection_for_bind( 

1553 engine, execution_options 

1554 ) 

1555 

1556 assert self._transaction is None 

1557 assert self.autocommit 

1558 conn = engine.connect(**kw) 

1559 if execution_options: 

1560 conn = conn.execution_options(**execution_options) 

1561 return conn 

1562 

1563 def execute( 

1564 self, 

1565 statement, 

1566 params=None, 

1567 execution_options=util.EMPTY_DICT, 

1568 bind_arguments=None, 

1569 _parent_execute_state=None, 

1570 _add_event=None, 

1571 **kw 

1572 ): 

1573 r"""Execute a SQL expression construct. 

1574 

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

1576 results of the statement execution. 

1577 

1578 E.g.:: 

1579 

1580 from sqlalchemy import select 

1581 result = session.execute( 

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

1583 ) 

1584 

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

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

1587 of :class:`_future.Connection`. 

1588 

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

1590 now the primary point of ORM statement execution when using 

1591 :term:`2.0 style` ORM usage. 

1592 

1593 :param statement: 

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

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

1596 

1597 :param params: 

1598 Optional dictionary, or list of dictionaries, containing 

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

1600 execution occurs; if a list of dictionaries, an 

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

1602 must correspond to parameter names present in the statement. 

1603 

1604 :param execution_options: optional dictionary of execution options, 

1605 which will be associated with the statement execution. This 

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

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

1608 provide additional options understood only in an ORM context. 

1609 

1610 :param bind_arguments: dictionary of additional arguments to determine 

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

1612 Contents of this dictionary are passed to the 

1613 :meth:`.Session.get_bind` method. 

1614 

1615 :param mapper: 

1616 deprecated; use the bind_arguments dictionary 

1617 

1618 :param bind: 

1619 deprecated; use the bind_arguments dictionary 

1620 

1621 :param \**kw: 

1622 deprecated; use the bind_arguments dictionary 

1623 

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

1625 

1626 

1627 """ 

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

1629 

1630 if kw: 

1631 util.warn_deprecated_20( 

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

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

1634 "Please use the bind_arguments parameter." 

1635 ) 

1636 if not bind_arguments: 

1637 bind_arguments = kw 

1638 else: 

1639 bind_arguments.update(kw) 

1640 elif not bind_arguments: 

1641 bind_arguments = {} 

1642 else: 

1643 bind_arguments = dict(bind_arguments) 

1644 

1645 if ( 

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

1647 == "orm" 

1648 ): 

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

1650 compile_state_cls = CompileState._get_plugin_class_for_plugin( 

1651 statement, "orm" 

1652 ) 

1653 else: 

1654 compile_state_cls = None 

1655 

1656 execution_options = util.coerce_to_immutabledict(execution_options) 

1657 

1658 if compile_state_cls is not None: 

1659 ( 

1660 statement, 

1661 execution_options, 

1662 ) = compile_state_cls.orm_pre_session_exec( 

1663 self, 

1664 statement, 

1665 params, 

1666 execution_options, 

1667 bind_arguments, 

1668 _parent_execute_state is not None, 

1669 ) 

1670 else: 

1671 bind_arguments.setdefault("clause", statement) 

1672 execution_options = execution_options.union( 

1673 {"future_result": True} 

1674 ) 

1675 

1676 if _parent_execute_state: 

1677 events_todo = _parent_execute_state._remaining_events() 

1678 else: 

1679 events_todo = self.dispatch.do_orm_execute 

1680 if _add_event: 

1681 events_todo = list(events_todo) + [_add_event] 

1682 

1683 if events_todo: 

1684 orm_exec_state = ORMExecuteState( 

1685 self, 

1686 statement, 

1687 params, 

1688 execution_options, 

1689 bind_arguments, 

1690 compile_state_cls, 

1691 events_todo, 

1692 ) 

1693 for idx, fn in enumerate(events_todo): 

1694 orm_exec_state._starting_event_idx = idx 

1695 result = fn(orm_exec_state) 

1696 if result: 

1697 return result 

1698 

1699 statement = orm_exec_state.statement 

1700 execution_options = orm_exec_state.local_execution_options 

1701 

1702 bind = self.get_bind(**bind_arguments) 

1703 

1704 if self.autocommit: 

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

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

1707 # interrelated 

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

1709 execution_options = execution_options.union( 

1710 dict(future_result=False) 

1711 ) 

1712 else: 

1713 conn = self._connection_for_bind(bind) 

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

1715 

1716 if compile_state_cls: 

1717 result = compile_state_cls.orm_setup_cursor_result( 

1718 self, 

1719 statement, 

1720 params, 

1721 execution_options, 

1722 bind_arguments, 

1723 result, 

1724 ) 

1725 

1726 return result 

1727 

1728 def scalar( 

1729 self, 

1730 statement, 

1731 params=None, 

1732 execution_options=util.EMPTY_DICT, 

1733 bind_arguments=None, 

1734 **kw 

1735 ): 

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

1737 

1738 Usage and parameters are the same as that of 

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

1740 value. 

1741 

1742 """ 

1743 

1744 return self.execute( 

1745 statement, 

1746 params=params, 

1747 execution_options=execution_options, 

1748 bind_arguments=bind_arguments, 

1749 **kw 

1750 ).scalar() 

1751 

1752 def scalars( 

1753 self, 

1754 statement, 

1755 params=None, 

1756 execution_options=util.EMPTY_DICT, 

1757 bind_arguments=None, 

1758 **kw 

1759 ): 

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

1761 

1762 Usage and parameters are the same as that of 

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

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

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

1766 

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

1768 

1769 .. versionadded:: 1.4.24 

1770 

1771 """ 

1772 

1773 return self.execute( 

1774 statement, 

1775 params=params, 

1776 execution_options=execution_options, 

1777 bind_arguments=bind_arguments, 

1778 **kw 

1779 ).scalars() 

1780 

1781 def close(self): 

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

1783 :class:`_orm.Session`. 

1784 

1785 This expunges all ORM objects associated with this 

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

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

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

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

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

1791 

1792 .. tip:: 

1793 

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

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

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

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

1798 and ORM objects. 

1799 

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

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

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

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

1804 

1805 .. seealso:: 

1806 

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

1808 :meth:`_orm.Session.close` 

1809 

1810 """ 

1811 self._close_impl(invalidate=False) 

1812 

1813 def invalidate(self): 

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

1815 

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

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

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

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

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

1821 multiple engines). 

1822 

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

1824 the connections are no longer safe to be used. 

1825 

1826 Below illustrates a scenario when using `gevent 

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

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

1829 

1830 import gevent 

1831 

1832 try: 

1833 sess = Session() 

1834 sess.add(User()) 

1835 sess.commit() 

1836 except gevent.Timeout: 

1837 sess.invalidate() 

1838 raise 

1839 except: 

1840 sess.rollback() 

1841 raise 

1842 

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

1844 does, including that all ORM objects are expunged. 

1845 

1846 """ 

1847 self._close_impl(invalidate=True) 

1848 

1849 def _close_impl(self, invalidate): 

1850 self.expunge_all() 

1851 if self._transaction is not None: 

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

1853 transaction.close(invalidate) 

1854 

1855 def expunge_all(self): 

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

1857 

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

1859 ``Session``. 

1860 

1861 """ 

1862 

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

1864 self.identity_map._kill() 

1865 self.identity_map = identity.WeakInstanceDict() 

1866 self._new = {} 

1867 self._deleted = {} 

1868 

1869 statelib.InstanceState._detach_states(all_states, self) 

1870 

1871 def _add_bind(self, key, bind): 

1872 try: 

1873 insp = inspect(key) 

1874 except sa_exc.NoInspectionAvailable as err: 

1875 if not isinstance(key, type): 

1876 util.raise_( 

1877 sa_exc.ArgumentError( 

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

1879 ), 

1880 replace_context=err, 

1881 ) 

1882 else: 

1883 self.__binds[key] = bind 

1884 else: 

1885 if insp.is_selectable: 

1886 self.__binds[insp] = bind 

1887 elif insp.is_mapper: 

1888 self.__binds[insp.class_] = bind 

1889 for _selectable in insp._all_tables: 

1890 self.__binds[_selectable] = bind 

1891 else: 

1892 raise sa_exc.ArgumentError( 

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

1894 ) 

1895 

1896 def bind_mapper(self, mapper, bind): 

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

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

1899 :class:`_engine.Connection`. 

1900 

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

1902 :meth:`.Session.get_bind` method. 

1903 

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

1905 or an instance of a mapped 

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

1907 classes. 

1908 

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

1910 object. 

1911 

1912 .. seealso:: 

1913 

1914 :ref:`session_partitioning` 

1915 

1916 :paramref:`.Session.binds` 

1917 

1918 :meth:`.Session.bind_table` 

1919 

1920 

1921 """ 

1922 self._add_bind(mapper, bind) 

1923 

1924 def bind_table(self, table, bind): 

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

1926 :class:`_engine.Engine` 

1927 or :class:`_engine.Connection`. 

1928 

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

1930 :meth:`.Session.get_bind` method. 

1931 

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

1933 which is typically the target 

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

1935 mapped. 

1936 

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

1938 object. 

1939 

1940 .. seealso:: 

1941 

1942 :ref:`session_partitioning` 

1943 

1944 :paramref:`.Session.binds` 

1945 

1946 :meth:`.Session.bind_mapper` 

1947 

1948 

1949 """ 

1950 self._add_bind(table, bind) 

1951 

1952 def get_bind( 

1953 self, 

1954 mapper=None, 

1955 clause=None, 

1956 bind=None, 

1957 _sa_skip_events=None, 

1958 _sa_skip_for_implicit_returning=False, 

1959 ): 

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

1961 

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

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

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

1965 

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

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

1968 appropriate bind to return. 

1969 

1970 Note that the "mapper" argument is usually present 

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

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

1973 individual INSERT/UPDATE/DELETE operation within a 

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

1975 

1976 The order of resolution is: 

1977 

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

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

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

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

1982 superclasses to more general. 

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

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

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

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

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

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

1989 associated with the clause. 

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

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

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

1993 selectable to which the mapper is mapped. 

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

1995 is raised. 

1996 

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

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

1999 of bind resolution scheme. See the example at 

2000 :ref:`session_custom_partitioning`. 

2001 

2002 :param mapper: 

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

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

2005 :class:`_orm.Mapper` 

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

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

2008 :class:`_schema.MetaData` 

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

2010 :class:`_orm.Mapper` 

2011 is mapped for a bind. 

2012 

2013 :param clause: 

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

2015 :func:`_expression.select`, 

2016 :func:`_expression.text`, 

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

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

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

2020 associated with 

2021 bound :class:`_schema.MetaData`. 

2022 

2023 .. seealso:: 

2024 

2025 :ref:`session_partitioning` 

2026 

2027 :paramref:`.Session.binds` 

2028 

2029 :meth:`.Session.bind_mapper` 

2030 

2031 :meth:`.Session.bind_table` 

2032 

2033 """ 

2034 

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

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

2037 if bind: 

2038 return bind 

2039 elif not self.__binds and self.bind: 

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

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

2042 return self.bind 

2043 

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

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

2046 # mapper and the clause 

2047 if mapper is clause is None: 

2048 if self.bind: 

2049 return self.bind 

2050 else: 

2051 raise sa_exc.UnboundExecutionError( 

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

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

2054 "a binding." 

2055 ) 

2056 

2057 # look more closely at the mapper. 

2058 if mapper is not None: 

2059 try: 

2060 mapper = inspect(mapper) 

2061 except sa_exc.NoInspectionAvailable as err: 

2062 if isinstance(mapper, type): 

2063 util.raise_( 

2064 exc.UnmappedClassError(mapper), 

2065 replace_context=err, 

2066 ) 

2067 else: 

2068 raise 

2069 

2070 # match up the mapper or clause in the __binds 

2071 if self.__binds: 

2072 # matching mappers and selectables to entries in the 

2073 # binds dictionary; supported use case. 

2074 if mapper: 

2075 for cls in mapper.class_.__mro__: 

2076 if cls in self.__binds: 

2077 return self.__binds[cls] 

2078 if clause is None: 

2079 clause = mapper.persist_selectable 

2080 

2081 if clause is not None: 

2082 plugin_subject = clause._propagate_attrs.get( 

2083 "plugin_subject", None 

2084 ) 

2085 

2086 if plugin_subject is not None: 

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

2088 if cls in self.__binds: 

2089 return self.__binds[cls] 

2090 

2091 for obj in visitors.iterate(clause): 

2092 if obj in self.__binds: 

2093 return self.__binds[obj] 

2094 

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

2096 # return that 

2097 if self.bind: 

2098 return self.bind 

2099 

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

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

2102 

2103 future_msg = "" 

2104 future_code = "" 

2105 

2106 if mapper and clause is None: 

2107 clause = mapper.persist_selectable 

2108 

2109 if clause is not None: 

2110 if clause.bind: 

2111 if self.future: 

2112 future_msg = ( 

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

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

2115 "bind is ignored." 

2116 ) 

2117 else: 

2118 util.warn_deprecated_20( 

2119 "This Session located a target engine via bound " 

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

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

2122 "to the Session() constructor directly." 

2123 ) 

2124 return clause.bind 

2125 

2126 if mapper: 

2127 if mapper.persist_selectable.bind: 

2128 if self.future: 

2129 future_msg = ( 

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

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

2132 "bind is ignored." 

2133 ) 

2134 else: 

2135 util.warn_deprecated_20( 

2136 "This Session located a target engine via bound " 

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

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

2139 "to the Session() constructor directly." 

2140 ) 

2141 return mapper.persist_selectable.bind 

2142 

2143 context = [] 

2144 if mapper is not None: 

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

2146 if clause is not None: 

2147 context.append("SQL expression") 

2148 

2149 raise sa_exc.UnboundExecutionError( 

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

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

2152 code=future_code, 

2153 ) 

2154 

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

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

2157 :class:`_orm.Session`. 

2158 

2159 """ 

2160 

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

2162 

2163 def _identity_lookup( 

2164 self, 

2165 mapper, 

2166 primary_key_identity, 

2167 identity_token=None, 

2168 passive=attributes.PASSIVE_OFF, 

2169 lazy_loaded_from=None, 

2170 ): 

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

2172 

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

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

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

2176 check if was deleted). 

2177 

2178 e.g.:: 

2179 

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

2181 

2182 :param mapper: mapper in use 

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

2184 a tuple. 

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

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

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

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

2189 :param passive: passive load flag passed to 

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

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

2192 if the flag allows for SQL to be emitted. 

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

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

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

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

2197 relationship-loaded). 

2198 

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

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

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

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

2203 

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

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

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

2207 :class:`_query.Query` object. 

2208 

2209 

2210 """ 

2211 

2212 key = mapper.identity_key_from_primary_key( 

2213 primary_key_identity, identity_token=identity_token 

2214 ) 

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

2216 

2217 @property 

2218 @util.contextmanager 

2219 def no_autoflush(self): 

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

2221 

2222 e.g.:: 

2223 

2224 with session.no_autoflush: 

2225 

2226 some_object = SomeClass() 

2227 session.add(some_object) 

2228 # won't autoflush 

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

2230 

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

2232 will not be subject to flushes occurring upon query 

2233 access. This is useful when initializing a series 

2234 of objects which involve existing database queries, 

2235 where the uncompleted object should not yet be flushed. 

2236 

2237 """ 

2238 autoflush = self.autoflush 

2239 self.autoflush = False 

2240 try: 

2241 yield self 

2242 finally: 

2243 self.autoflush = autoflush 

2244 

2245 def _autoflush(self): 

2246 if self.autoflush and not self._flushing: 

2247 try: 

2248 self.flush() 

2249 except sa_exc.StatementError as e: 

2250 # note we are reraising StatementError as opposed to 

2251 # raising FlushError with "chaining" to remain compatible 

2252 # with code that catches StatementError, IntegrityError, 

2253 # etc. 

2254 e.add_detail( 

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

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

2257 "flush is occurring prematurely" 

2258 ) 

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

2260 

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

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

2263 

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

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

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

2267 value available in the current transaction. 

2268 

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

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

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

2272 Unloaded relationship attributes will remain unloaded, as will 

2273 relationship attributes that were originally lazy loaded. 

2274 

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

2276 can also refresh eagerly loaded attributes. 

2277 

2278 .. tip:: 

2279 

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

2281 refreshing both column and relationship oriented attributes, its 

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

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

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

2285 once while having explicit control over relationship loader 

2286 strategies, use the 

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

2288 instead. 

2289 

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

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

2292 in database state outside of that transaction. Refreshing 

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

2294 where database rows have not yet been accessed. 

2295 

2296 :param attribute_names: optional. An iterable collection of 

2297 string attribute names indicating a subset of attributes to 

2298 be refreshed. 

2299 

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

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

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

2303 flags should match the parameters of 

2304 :meth:`_query.Query.with_for_update`. 

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

2306 

2307 .. seealso:: 

2308 

2309 :ref:`session_expire` - introductory material 

2310 

2311 :meth:`.Session.expire` 

2312 

2313 :meth:`.Session.expire_all` 

2314 

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

2316 to refresh objects as they would be loaded normally. 

2317 

2318 """ 

2319 try: 

2320 state = attributes.instance_state(instance) 

2321 except exc.NO_STATE as err: 

2322 util.raise_( 

2323 exc.UnmappedInstanceError(instance), 

2324 replace_context=err, 

2325 ) 

2326 

2327 self._expire_state(state, attribute_names) 

2328 

2329 if with_for_update == {}: 

2330 raise sa_exc.ArgumentError( 

2331 "with_for_update should be the boolean value " 

2332 "True, or a dictionary with options. " 

2333 "A blank dictionary is ambiguous." 

2334 ) 

2335 

2336 with_for_update = query.ForUpdateArg._from_argument(with_for_update) 

2337 

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

2339 if ( 

2340 loading.load_on_ident( 

2341 self, 

2342 stmt, 

2343 state.key, 

2344 refresh_state=state, 

2345 with_for_update=with_for_update, 

2346 only_load_props=attribute_names, 

2347 ) 

2348 is None 

2349 ): 

2350 raise sa_exc.InvalidRequestError( 

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

2352 ) 

2353 

2354 def expire_all(self): 

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

2356 

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

2358 a query will be issued using the 

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

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

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

2362 previously read in that same transaction, regardless of changes 

2363 in database state outside of that transaction. 

2364 

2365 To expire individual objects and individual attributes 

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

2367 

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

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

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

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

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

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

2374 

2375 .. seealso:: 

2376 

2377 :ref:`session_expire` - introductory material 

2378 

2379 :meth:`.Session.expire` 

2380 

2381 :meth:`.Session.refresh` 

2382 

2383 :meth:`_orm.Query.populate_existing` 

2384 

2385 """ 

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

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

2388 

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

2390 """Expire the attributes on an instance. 

2391 

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

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

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

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

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

2397 previously read in that same transaction, regardless of changes 

2398 in database state outside of that transaction. 

2399 

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

2401 use :meth:`Session.expire_all`. 

2402 

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

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

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

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

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

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

2409 transaction. 

2410 

2411 :param instance: The instance to be refreshed. 

2412 :param attribute_names: optional list of string attribute names 

2413 indicating a subset of attributes to be expired. 

2414 

2415 .. seealso:: 

2416 

2417 :ref:`session_expire` - introductory material 

2418 

2419 :meth:`.Session.expire` 

2420 

2421 :meth:`.Session.refresh` 

2422 

2423 :meth:`_orm.Query.populate_existing` 

2424 

2425 """ 

2426 try: 

2427 state = attributes.instance_state(instance) 

2428 except exc.NO_STATE as err: 

2429 util.raise_( 

2430 exc.UnmappedInstanceError(instance), 

2431 replace_context=err, 

2432 ) 

2433 self._expire_state(state, attribute_names) 

2434 

2435 def _expire_state(self, state, attribute_names): 

2436 self._validate_persistent(state) 

2437 if attribute_names: 

2438 state._expire_attributes(state.dict, attribute_names) 

2439 else: 

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

2441 # remove associations 

2442 cascaded = list( 

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

2444 ) 

2445 self._conditional_expire(state) 

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

2447 self._conditional_expire(st_) 

2448 

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

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

2451 

2452 if state.key: 

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

2454 elif state in self._new: 

2455 self._new.pop(state) 

2456 state._detach(self) 

2457 

2458 def expunge(self, instance): 

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

2460 

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

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

2463 

2464 """ 

2465 try: 

2466 state = attributes.instance_state(instance) 

2467 except exc.NO_STATE as err: 

2468 util.raise_( 

2469 exc.UnmappedInstanceError(instance), 

2470 replace_context=err, 

2471 ) 

2472 if state.session_id is not self.hash_key: 

2473 raise sa_exc.InvalidRequestError( 

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

2475 ) 

2476 

2477 cascaded = list( 

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

2479 ) 

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

2481 

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

2483 for state in states: 

2484 if state in self._new: 

2485 self._new.pop(state) 

2486 elif self.identity_map.contains_state(state): 

2487 self.identity_map.safe_discard(state) 

2488 self._deleted.pop(state, None) 

2489 elif self._transaction: 

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

2491 # in the transaction snapshot 

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

2493 statelib.InstanceState._detach_states( 

2494 states, self, to_transient=to_transient 

2495 ) 

2496 

2497 def _register_persistent(self, states): 

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

2499 

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

2501 state as well as already persistent objects. 

2502 

2503 """ 

2504 

2505 pending_to_persistent = self.dispatch.pending_to_persistent or None 

2506 for state in states: 

2507 mapper = _state_mapper(state) 

2508 

2509 # prevent against last minute dereferences of the object 

2510 obj = state.obj() 

2511 if obj is not None: 

2512 

2513 instance_key = mapper._identity_key_from_state(state) 

2514 

2515 if ( 

2516 _none_set.intersection(instance_key[1]) 

2517 and not mapper.allow_partial_pks 

2518 or _none_set.issuperset(instance_key[1]) 

2519 ): 

2520 raise exc.FlushError( 

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

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

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

2524 "that the mapped Column object is configured to " 

2525 "expect these generated values. Ensure also that " 

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

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

2528 % state_str(state) 

2529 ) 

2530 

2531 if state.key is None: 

2532 state.key = instance_key 

2533 elif state.key != instance_key: 

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

2535 # state has already replaced this one in the identity 

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

2537 self.identity_map.safe_discard(state) 

2538 if state in self._transaction._key_switches: 

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

2540 else: 

2541 orig_key = state.key 

2542 self._transaction._key_switches[state] = ( 

2543 orig_key, 

2544 instance_key, 

2545 ) 

2546 state.key = instance_key 

2547 

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

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

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

2551 old = self.identity_map.replace(state) 

2552 if ( 

2553 old is not None 

2554 and mapper._identity_key_from_state(old) == instance_key 

2555 and old.obj() is not None 

2556 ): 

2557 util.warn( 

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

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

2560 "load operations occurring inside of an event handler " 

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

2562 ) 

2563 state._orphaned_outside_of_session = False 

2564 

2565 statelib.InstanceState._commit_all_states( 

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

2567 ) 

2568 

2569 self._register_altered(states) 

2570 

2571 if pending_to_persistent is not None: 

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

2573 pending_to_persistent(self, state) 

2574 

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

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

2577 self._new.pop(state) 

2578 

2579 def _register_altered(self, states): 

2580 if self._transaction: 

2581 for state in states: 

2582 if state in self._new: 

2583 self._transaction._new[state] = True 

2584 else: 

2585 self._transaction._dirty[state] = True 

2586 

2587 def _remove_newly_deleted(self, states): 

2588 persistent_to_deleted = self.dispatch.persistent_to_deleted or None 

2589 for state in states: 

2590 if self._transaction: 

2591 self._transaction._deleted[state] = True 

2592 

2593 if persistent_to_deleted is not None: 

2594 # get a strong reference before we pop out of 

2595 # self._deleted 

2596 obj = state.obj() # noqa 

2597 

2598 self.identity_map.safe_discard(state) 

2599 self._deleted.pop(state, None) 

2600 state._deleted = True 

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

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

2603 # tracked as part of that 

2604 if persistent_to_deleted is not None: 

2605 persistent_to_deleted(self, state) 

2606 

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

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

2609 

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

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

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

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

2614 

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

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

2617 state directly. 

2618 

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

2620 objects which were transient when they were passed to 

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

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

2623 :class:`_orm.Session`. 

2624 

2625 .. seealso:: 

2626 

2627 :meth:`_orm.Session.add_all` 

2628 

2629 :ref:`session_adding` - at :ref:`session_basics` 

2630 

2631 """ 

2632 if _warn and self._warn_on_events: 

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

2634 

2635 try: 

2636 state = attributes.instance_state(instance) 

2637 except exc.NO_STATE as err: 

2638 util.raise_( 

2639 exc.UnmappedInstanceError(instance), 

2640 replace_context=err, 

2641 ) 

2642 

2643 self._save_or_update_state(state) 

2644 

2645 def add_all(self, instances): 

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

2647 

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

2649 behavioral description. 

2650 

2651 .. seealso:: 

2652 

2653 :meth:`_orm.Session.add` 

2654 

2655 :ref:`session_adding` - at :ref:`session_basics` 

2656 

2657 """ 

2658 

2659 if self._warn_on_events: 

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

2661 

2662 for instance in instances: 

2663 self.add(instance, _warn=False) 

2664 

2665 def _save_or_update_state(self, state): 

2666 state._orphaned_outside_of_session = False 

2667 self._save_or_update_impl(state) 

2668 

2669 mapper = _state_mapper(state) 

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

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

2672 ): 

2673 self._save_or_update_impl(st_) 

2674 

2675 def delete(self, instance): 

2676 """Mark an instance as deleted. 

2677 

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

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

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

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

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

2683 

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

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

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

2687 is successfully committed, 

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

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

2690 

2691 .. seealso:: 

2692 

2693 :ref:`session_deleting` - at :ref:`session_basics` 

2694 

2695 """ 

2696 if self._warn_on_events: 

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

2698 

2699 try: 

2700 state = attributes.instance_state(instance) 

2701 except exc.NO_STATE as err: 

2702 util.raise_( 

2703 exc.UnmappedInstanceError(instance), 

2704 replace_context=err, 

2705 ) 

2706 

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

2708 

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

2710 

2711 if state.key is None: 

2712 if head: 

2713 raise sa_exc.InvalidRequestError( 

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

2715 ) 

2716 else: 

2717 return 

2718 

2719 to_attach = self._before_attach(state, obj) 

2720 

2721 if state in self._deleted: 

2722 return 

2723 

2724 self.identity_map.add(state) 

2725 

2726 if to_attach: 

2727 self._after_attach(state, obj) 

2728 

2729 if head: 

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

2731 # so that autoflush does not delete the item 

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

2733 cascade_states = list( 

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

2735 ) 

2736 

2737 self._deleted[state] = obj 

2738 

2739 if head: 

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

2741 self._delete_impl(st_, o, False) 

2742 

2743 def get( 

2744 self, 

2745 entity, 

2746 ident, 

2747 options=None, 

2748 populate_existing=False, 

2749 with_for_update=None, 

2750 identity_token=None, 

2751 execution_options=None, 

2752 ): 

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

2754 or ``None`` if not found. 

2755 

2756 E.g.:: 

2757 

2758 my_user = session.get(User, 5) 

2759 

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

2761 

2762 some_object = session.get( 

2763 VersionedFoo, 

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

2765 ) 

2766 

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

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

2769 

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

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

2772 If the given primary key identifier is present 

2773 in the local identity map, the object is returned 

2774 directly from this collection and no SQL is emitted, 

2775 unless the object has been marked fully expired. 

2776 If not present, 

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

2778 

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

2780 the object is present in the identity map and 

2781 marked as expired - a SELECT 

2782 is emitted to refresh the object as well as to 

2783 ensure that the row is still present. 

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

2785 

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

2787 type of entity to be loaded. 

2788 

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

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

2791 a tuple or dictionary should be passed. 

2792 

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

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

2795 the call looks like:: 

2796 

2797 my_object = session.get(SomeClass, 5) 

2798 

2799 The tuple form contains primary key values typically in 

2800 the order in which they correspond to the mapped 

2801 :class:`_schema.Table` 

2802 object's primary key columns, or if the 

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

2804 used, in 

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

2806 of a row is represented by the integer 

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

2808 

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

2810 

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

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

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

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

2815 

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

2817 

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

2819 applied to the query, if one is emitted. 

2820 

2821 :param populate_existing: causes the method to unconditionally emit 

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

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

2824 

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

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

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

2828 flags should match the parameters of 

2829 :meth:`_query.Query.with_for_update`. 

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

2831 

2832 :param execution_options: optional dictionary of execution options, 

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

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

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

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

2837 

2838 .. versionadded:: 1.4.29 

2839 

2840 .. seealso:: 

2841 

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

2843 options 

2844 

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

2846 

2847 """ 

2848 return self._get_impl( 

2849 entity, 

2850 ident, 

2851 loading.load_on_pk_identity, 

2852 options, 

2853 populate_existing=populate_existing, 

2854 with_for_update=with_for_update, 

2855 identity_token=identity_token, 

2856 execution_options=execution_options, 

2857 ) 

2858 

2859 def _get_impl( 

2860 self, 

2861 entity, 

2862 primary_key_identity, 

2863 db_load_fn, 

2864 options=None, 

2865 populate_existing=False, 

2866 with_for_update=None, 

2867 identity_token=None, 

2868 execution_options=None, 

2869 ): 

2870 

2871 # convert composite types to individual args 

2872 if hasattr(primary_key_identity, "__composite_values__"): 

2873 primary_key_identity = primary_key_identity.__composite_values__() 

2874 

2875 mapper = inspect(entity) 

2876 

2877 if not mapper or not mapper.is_mapper: 

2878 raise sa_exc.ArgumentError( 

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

2880 ) 

2881 

2882 is_dict = isinstance(primary_key_identity, dict) 

2883 if not is_dict: 

2884 primary_key_identity = util.to_list( 

2885 primary_key_identity, default=(None,) 

2886 ) 

2887 

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

2889 raise sa_exc.InvalidRequestError( 

2890 "Incorrect number of values in identifier to formulate " 

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

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

2893 ) 

2894 

2895 if is_dict: 

2896 

2897 pk_synonyms = mapper._pk_synonyms 

2898 

2899 if pk_synonyms: 

2900 correct_keys = set(pk_synonyms).intersection( 

2901 primary_key_identity 

2902 ) 

2903 

2904 if correct_keys: 

2905 primary_key_identity = dict(primary_key_identity) 

2906 for k in correct_keys: 

2907 primary_key_identity[ 

2908 pk_synonyms[k] 

2909 ] = primary_key_identity[k] 

2910 

2911 try: 

2912 primary_key_identity = list( 

2913 primary_key_identity[prop.key] 

2914 for prop in mapper._identity_key_props 

2915 ) 

2916 

2917 except KeyError as err: 

2918 util.raise_( 

2919 sa_exc.InvalidRequestError( 

2920 "Incorrect names of values in identifier to formulate " 

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

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

2923 % ",".join( 

2924 "'%s'" % prop.key 

2925 for prop in mapper._identity_key_props 

2926 ) 

2927 ), 

2928 replace_context=err, 

2929 ) 

2930 

2931 if ( 

2932 not populate_existing 

2933 and not mapper.always_refresh 

2934 and with_for_update is None 

2935 ): 

2936 

2937 instance = self._identity_lookup( 

2938 mapper, primary_key_identity, identity_token=identity_token 

2939 ) 

2940 

2941 if instance is not None: 

2942 # reject calls for id in identity map but class 

2943 # mismatch. 

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

2945 return None 

2946 return instance 

2947 elif instance is attributes.PASSIVE_CLASS_MISMATCH: 

2948 return None 

2949 

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

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

2952 # asserted in a lot of unit tests :) 

2953 

2954 load_options = context.QueryContext.default_load_options 

2955 

2956 if populate_existing: 

2957 load_options += {"_populate_existing": populate_existing} 

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

2959 LABEL_STYLE_TABLENAME_PLUS_COL 

2960 ) 

2961 if with_for_update is not None: 

2962 statement._for_update_arg = query.ForUpdateArg._from_argument( 

2963 with_for_update 

2964 ) 

2965 

2966 if options: 

2967 statement = statement.options(*options) 

2968 if execution_options: 

2969 statement = statement.execution_options(**execution_options) 

2970 return db_load_fn( 

2971 self, 

2972 statement, 

2973 primary_key_identity, 

2974 load_options=load_options, 

2975 ) 

2976 

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

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

2979 within this :class:`.Session`. 

2980 

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

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

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

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

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

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

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

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

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

2990 

2991 This operation cascades to associated instances if the association is 

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

2993 

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

2995 

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

2997 pending objects with overlapping primary keys in the same way 

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

2999 

3000 :param instance: Instance to be merged. 

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

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

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

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

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

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

3007 without re-querying the database. 

3008 

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

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

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

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

3013 the merge operation populates local attributes and 

3014 cascades to related objects and 

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

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

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

3018 any existing related objects or collections that might not 

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

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

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

3022 method. 

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

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

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

3026 

3027 .. versionadded:: 1.4.24 

3028 

3029 

3030 .. seealso:: 

3031 

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

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

3034 

3035 """ 

3036 

3037 if self._warn_on_events: 

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

3039 

3040 _recursive = {} 

3041 _resolve_conflict_map = {} 

3042 

3043 if load: 

3044 # flush current contents if we expect to load data 

3045 self._autoflush() 

3046 

3047 object_mapper(instance) # verify mapped 

3048 autoflush = self.autoflush 

3049 try: 

3050 self.autoflush = False 

3051 return self._merge( 

3052 attributes.instance_state(instance), 

3053 attributes.instance_dict(instance), 

3054 load=load, 

3055 options=options, 

3056 _recursive=_recursive, 

3057 _resolve_conflict_map=_resolve_conflict_map, 

3058 ) 

3059 finally: 

3060 self.autoflush = autoflush 

3061 

3062 def _merge( 

3063 self, 

3064 state, 

3065 state_dict, 

3066 load=True, 

3067 options=None, 

3068 _recursive=None, 

3069 _resolve_conflict_map=None, 

3070 ): 

3071 mapper = _state_mapper(state) 

3072 if state in _recursive: 

3073 return _recursive[state] 

3074 

3075 new_instance = False 

3076 key = state.key 

3077 

3078 if key is None: 

3079 if state in self._new: 

3080 util.warn( 

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

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

3083 "to do" % state_str(state) 

3084 ) 

3085 

3086 if not load: 

3087 raise sa_exc.InvalidRequestError( 

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

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

3090 "all changes on mapped instances before merging with " 

3091 "load=False." 

3092 ) 

3093 key = mapper._identity_key_from_state(state) 

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

3095 not _none_set.intersection(key[1]) 

3096 or ( 

3097 mapper.allow_partial_pks 

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

3099 ) 

3100 ) 

3101 else: 

3102 key_is_persistent = True 

3103 

3104 if key in self.identity_map: 

3105 try: 

3106 merged = self.identity_map[key] 

3107 except KeyError: 

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

3109 merged = None 

3110 else: 

3111 merged = None 

3112 

3113 if merged is None: 

3114 if key_is_persistent and key in _resolve_conflict_map: 

3115 merged = _resolve_conflict_map[key] 

3116 

3117 elif not load: 

3118 if state.modified: 

3119 raise sa_exc.InvalidRequestError( 

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

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

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

3123 ) 

3124 merged = mapper.class_manager.new_instance() 

3125 merged_state = attributes.instance_state(merged) 

3126 merged_state.key = key 

3127 self._update_impl(merged_state) 

3128 new_instance = True 

3129 

3130 elif key_is_persistent: 

3131 merged = self.get( 

3132 mapper.class_, 

3133 key[1], 

3134 identity_token=key[2], 

3135 options=options, 

3136 ) 

3137 

3138 if merged is None: 

3139 merged = mapper.class_manager.new_instance() 

3140 merged_state = attributes.instance_state(merged) 

3141 merged_dict = attributes.instance_dict(merged) 

3142 new_instance = True 

3143 self._save_or_update_state(merged_state) 

3144 else: 

3145 merged_state = attributes.instance_state(merged) 

3146 merged_dict = attributes.instance_dict(merged) 

3147 

3148 _recursive[state] = merged 

3149 _resolve_conflict_map[key] = merged 

3150 

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

3152 # state out. 

3153 if state is not merged_state: 

3154 # version check if applicable 

3155 if mapper.version_id_col is not None: 

3156 existing_version = mapper._get_state_attr_by_column( 

3157 state, 

3158 state_dict, 

3159 mapper.version_id_col, 

3160 passive=attributes.PASSIVE_NO_INITIALIZE, 

3161 ) 

3162 

3163 merged_version = mapper._get_state_attr_by_column( 

3164 merged_state, 

3165 merged_dict, 

3166 mapper.version_id_col, 

3167 passive=attributes.PASSIVE_NO_INITIALIZE, 

3168 ) 

3169 

3170 if ( 

3171 existing_version is not attributes.PASSIVE_NO_RESULT 

3172 and merged_version is not attributes.PASSIVE_NO_RESULT 

3173 and existing_version != merged_version 

3174 ): 

3175 raise exc.StaleDataError( 

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

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

3178 "Leave the version attribute unset when " 

3179 "merging to update the most recent version." 

3180 % ( 

3181 existing_version, 

3182 state_str(merged_state), 

3183 merged_version, 

3184 ) 

3185 ) 

3186 

3187 merged_state.load_path = state.load_path 

3188 merged_state.load_options = state.load_options 

3189 

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

3191 # the callables_ that would have been generated by those 

3192 # load_options. 

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

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

3195 merged_state._copy_callables(state) 

3196 

3197 for prop in mapper.iterate_properties: 

3198 prop.merge( 

3199 self, 

3200 state, 

3201 state_dict, 

3202 merged_state, 

3203 merged_dict, 

3204 load, 

3205 _recursive, 

3206 _resolve_conflict_map, 

3207 ) 

3208 

3209 if not load: 

3210 # remove any history 

3211 merged_state._commit_all(merged_dict, self.identity_map) 

3212 merged_state.manager.dispatch._sa_event_merge_wo_load( 

3213 merged_state, None 

3214 ) 

3215 

3216 if new_instance: 

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

3218 return merged 

3219 

3220 def _validate_persistent(self, state): 

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

3222 raise sa_exc.InvalidRequestError( 

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

3224 % state_str(state) 

3225 ) 

3226 

3227 def _save_impl(self, state): 

3228 if state.key is not None: 

3229 raise sa_exc.InvalidRequestError( 

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

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

3232 ) 

3233 

3234 obj = state.obj() 

3235 to_attach = self._before_attach(state, obj) 

3236 if state not in self._new: 

3237 self._new[state] = obj 

3238 state.insert_order = len(self._new) 

3239 if to_attach: 

3240 self._after_attach(state, obj) 

3241 

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

3243 if state.key is None: 

3244 raise sa_exc.InvalidRequestError( 

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

3246 ) 

3247 

3248 if state._deleted: 

3249 if revert_deletion: 

3250 if not state._attached: 

3251 return 

3252 del state._deleted 

3253 else: 

3254 raise sa_exc.InvalidRequestError( 

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

3256 "Use the make_transient() " 

3257 "function to send this object back " 

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

3259 ) 

3260 

3261 obj = state.obj() 

3262 

3263 # check for late gc 

3264 if obj is None: 

3265 return 

3266 

3267 to_attach = self._before_attach(state, obj) 

3268 

3269 self._deleted.pop(state, None) 

3270 if revert_deletion: 

3271 self.identity_map.replace(state) 

3272 else: 

3273 self.identity_map.add(state) 

3274 

3275 if to_attach: 

3276 self._after_attach(state, obj) 

3277 elif revert_deletion: 

3278 self.dispatch.deleted_to_persistent(self, state) 

3279 

3280 def _save_or_update_impl(self, state): 

3281 if state.key is None: 

3282 self._save_impl(state) 

3283 else: 

3284 self._update_impl(state) 

3285 

3286 def enable_relationship_loading(self, obj): 

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

3288 object loading. 

3289 

3290 .. warning:: 

3291 

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

3293 use cases and is not recommended for general use. 

3294 

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

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

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

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

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

3300 will be unavailable. 

3301 

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

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

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

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

3306 

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

3308 Altering a relationship-bound attribute on the target object 

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

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

3311 

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

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

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

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

3316 related items. 

3317 

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

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

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

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

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

3323 

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

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

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

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

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

3329 

3330 .. seealso:: 

3331 

3332 :paramref:`_orm.relationship.load_on_pending` - this flag 

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

3334 are pending. 

3335 

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

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

3338 will unexpire attributes on access. 

3339 

3340 """ 

3341 try: 

3342 state = attributes.instance_state(obj) 

3343 except exc.NO_STATE as err: 

3344 util.raise_( 

3345 exc.UnmappedInstanceError(obj), 

3346 replace_context=err, 

3347 ) 

3348 

3349 to_attach = self._before_attach(state, obj) 

3350 state._load_pending = True 

3351 if to_attach: 

3352 self._after_attach(state, obj) 

3353 

3354 def _before_attach(self, state, obj): 

3355 self._autobegin() 

3356 

3357 if state.session_id == self.hash_key: 

3358 return False 

3359 

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

3361 raise sa_exc.InvalidRequestError( 

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

3363 "(this is '%s')" 

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

3365 ) 

3366 

3367 self.dispatch.before_attach(self, state) 

3368 

3369 return True 

3370 

3371 def _after_attach(self, state, obj): 

3372 state.session_id = self.hash_key 

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

3374 state._strong_obj = obj 

3375 self.dispatch.after_attach(self, state) 

3376 

3377 if state.key: 

3378 self.dispatch.detached_to_persistent(self, state) 

3379 else: 

3380 self.dispatch.transient_to_pending(self, state) 

3381 

3382 def __contains__(self, instance): 

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

3384 

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

3386 result of True. 

3387 

3388 """ 

3389 try: 

3390 state = attributes.instance_state(instance) 

3391 except exc.NO_STATE as err: 

3392 util.raise_( 

3393 exc.UnmappedInstanceError(instance), 

3394 replace_context=err, 

3395 ) 

3396 return self._contains_state(state) 

3397 

3398 def __iter__(self): 

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

3400 Session. 

3401 

3402 """ 

3403 return iter( 

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

3405 ) 

3406 

3407 def _contains_state(self, state): 

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

3409 

3410 def flush(self, objects=None): 

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

3412 

3413 Writes out all pending object creations, deletions and modifications 

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

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

3416 solver. 

3417 

3418 Database operations will be issued in the current transactional 

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

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

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

3422 changes from Python to the database's transaction buffer. 

3423 

3424 For ``autocommit`` Sessions with no active manual transaction, flush() 

3425 will create a transaction on the fly that surrounds the entire set of 

3426 operations into the flush. 

3427 

3428 :param objects: Optional; restricts the flush operation to operate 

3429 only on elements that are in the given collection. 

3430 

3431 This feature is for an extremely narrow set of use cases where 

3432 particular objects may need to be operated upon before the 

3433 full flush() occurs. It is not intended for general use. 

3434 

3435 """ 

3436 

3437 if self._flushing: 

3438 raise sa_exc.InvalidRequestError("Session is already flushing") 

3439 

3440 if self._is_clean(): 

3441 return 

3442 try: 

3443 self._flushing = True 

3444 self._flush(objects) 

3445 finally: 

3446 self._flushing = False 

3447 

3448 def _flush_warning(self, method): 

3449 util.warn( 

3450 "Usage of the '%s' operation is not currently supported " 

3451 "within the execution stage of the flush process. " 

3452 "Results may not be consistent. Consider using alternative " 

3453 "event listeners or connection-level operations instead." % method 

3454 ) 

3455 

3456 def _is_clean(self): 

3457 return ( 

3458 not self.identity_map.check_modified() 

3459 and not self._deleted 

3460 and not self._new 

3461 ) 

3462 

3463 def _flush(self, objects=None): 

3464 

3465 dirty = self._dirty_states 

3466 if not dirty and not self._deleted and not self._new: 

3467 self.identity_map._modified.clear() 

3468 return 

3469 

3470 flush_context = UOWTransaction(self) 

3471 

3472 if self.dispatch.before_flush: 

3473 self.dispatch.before_flush(self, flush_context, objects) 

3474 # re-establish "dirty states" in case the listeners 

3475 # added 

3476 dirty = self._dirty_states 

3477 

3478 deleted = set(self._deleted) 

3479 new = set(self._new) 

3480 

3481 dirty = set(dirty).difference(deleted) 

3482 

3483 # create the set of all objects we want to operate upon 

3484 if objects: 

3485 # specific list passed in 

3486 objset = set() 

3487 for o in objects: 

3488 try: 

3489 state = attributes.instance_state(o) 

3490 

3491 except exc.NO_STATE as err: 

3492 util.raise_( 

3493 exc.UnmappedInstanceError(o), 

3494 replace_context=err, 

3495 ) 

3496 objset.add(state) 

3497 else: 

3498 objset = None 

3499 

3500 # store objects whose fate has been decided 

3501 processed = set() 

3502 

3503 # put all saves/updates into the flush context. detect top-level 

3504 # orphans and throw them into deleted. 

3505 if objset: 

3506 proc = new.union(dirty).intersection(objset).difference(deleted) 

3507 else: 

3508 proc = new.union(dirty).difference(deleted) 

3509 

3510 for state in proc: 

3511 is_orphan = _state_mapper(state)._is_orphan(state) 

3512 

3513 is_persistent_orphan = is_orphan and state.has_identity 

3514 

3515 if ( 

3516 is_orphan 

3517 and not is_persistent_orphan 

3518 and state._orphaned_outside_of_session 

3519 ): 

3520 self._expunge_states([state]) 

3521 else: 

3522 _reg = flush_context.register_object( 

3523 state, isdelete=is_persistent_orphan 

3524 ) 

3525 assert _reg, "Failed to add object to the flush context!" 

3526 processed.add(state) 

3527 

3528 # put all remaining deletes into the flush context. 

3529 if objset: 

3530 proc = deleted.intersection(objset).difference(processed) 

3531 else: 

3532 proc = deleted.difference(processed) 

3533 for state in proc: 

3534 _reg = flush_context.register_object(state, isdelete=True) 

3535 assert _reg, "Failed to add object to the flush context!" 

3536 

3537 if not flush_context.has_work: 

3538 return 

3539 

3540 flush_context.transaction = transaction = self.begin(_subtrans=True) 

3541 try: 

3542 self._warn_on_events = True 

3543 try: 

3544 flush_context.execute() 

3545 finally: 

3546 self._warn_on_events = False 

3547 

3548 self.dispatch.after_flush(self, flush_context) 

3549 

3550 flush_context.finalize_flush_changes() 

3551 

3552 if not objects and self.identity_map._modified: 

3553 len_ = len(self.identity_map._modified) 

3554 

3555 statelib.InstanceState._commit_all_states( 

3556 [ 

3557 (state, state.dict) 

3558 for state in self.identity_map._modified 

3559 ], 

3560 instance_dict=self.identity_map, 

3561 ) 

3562 util.warn( 

3563 "Attribute history events accumulated on %d " 

3564 "previously clean instances " 

3565 "within inner-flush event handlers have been " 

3566 "reset, and will not result in database updates. " 

3567 "Consider using set_committed_value() within " 

3568 "inner-flush event handlers to avoid this warning." % len_ 

3569 ) 

3570 

3571 # useful assertions: 

3572 # if not objects: 

3573 # assert not self.identity_map._modified 

3574 # else: 

3575 # assert self.identity_map._modified == \ 

3576 # self.identity_map._modified.difference(objects) 

3577 

3578 self.dispatch.after_flush_postexec(self, flush_context) 

3579 

3580 transaction.commit() 

3581 

3582 except: 

3583 with util.safe_reraise(): 

3584 transaction.rollback(_capture_exception=True) 

3585 

3586 def bulk_save_objects( 

3587 self, 

3588 objects, 

3589 return_defaults=False, 

3590 update_changed_only=True, 

3591 preserve_order=True, 

3592 ): 

3593 """Perform a bulk save of the given list of objects. 

3594 

3595 The bulk save feature allows mapped objects to be used as the 

3596 source of simple INSERT and UPDATE operations which can be more easily 

3597 grouped together into higher performing "executemany" 

3598 operations; the extraction of data from the objects is also performed 

3599 using a lower-latency process that ignores whether or not attributes 

3600 have actually been modified in the case of UPDATEs, and also ignores 

3601 SQL expressions. 

3602 

3603 The objects as given are not added to the session and no additional 

3604 state is established on them. If the 

3605 :paramref:`_orm.Session.bulk_save_objects.return_defaults` flag is set, 

3606 then server-generated primary key values will be assigned to the 

3607 returned objects, but **not server side defaults**; this is a 

3608 limitation in the implementation. If stateful objects are desired, 

3609 please use the standard :meth:`_orm.Session.add_all` approach or 

3610 as an alternative newer mass-insert features such as 

3611 :ref:`orm_dml_returning_objects`. 

3612 

3613 .. legacy:: 

3614 

3615 The bulk save feature allows for a lower-latency INSERT/UPDATE 

3616 of rows at the expense of most other unit-of-work features. 

3617 Features such as object management, relationship handling, 

3618 and SQL clause support are silently omitted in favor of raw 

3619 INSERT/UPDATES of records. 

3620 

3621 In SQLAlchemy 2.0, improved versions of the bulk insert/update 

3622 methods are introduced, with clearer behavior and 

3623 documentation, new capabilities, and much better performance. 

3624 

3625 For 1.4 use, **please read the list of caveats at** 

3626 :ref:`bulk_operations_caveats` **before using this method, and 

3627 fully test and confirm the functionality of all code developed 

3628 using these systems.** 

3629 

3630 :param objects: a sequence of mapped object instances. The mapped 

3631 objects are persisted as is, and are **not** associated with the 

3632 :class:`.Session` afterwards. 

3633 

3634 For each object, whether the object is sent as an INSERT or an 

3635 UPDATE is dependent on the same rules used by the :class:`.Session` 

3636 in traditional operation; if the object has the 

3637 :attr:`.InstanceState.key` 

3638 attribute set, then the object is assumed to be "detached" and 

3639 will result in an UPDATE. Otherwise, an INSERT is used. 

3640 

3641 In the case of an UPDATE, statements are grouped based on which 

3642 attributes have changed, and are thus to be the subject of each 

3643 SET clause. If ``update_changed_only`` is False, then all 

3644 attributes present within each object are applied to the UPDATE 

3645 statement, which may help in allowing the statements to be grouped 

3646 together into a larger executemany(), and will also reduce the 

3647 overhead of checking history on attributes. 

3648 

3649 :param return_defaults: when True, rows that are missing values which 

3650 generate defaults, namely integer primary key defaults and sequences, 

3651 will be inserted **one at a time**, so that the primary key value 

3652 is available. In particular this will allow joined-inheritance 

3653 and other multi-table mappings to insert correctly without the need 

3654 to provide primary key values ahead of time; however, 

3655 :paramref:`.Session.bulk_save_objects.return_defaults` **greatly 

3656 reduces the performance gains** of the method overall. It is strongly 

3657 advised to please use the standard :meth:`_orm.Session.add_all` 

3658 approach. 

3659 

3660 :param update_changed_only: when True, UPDATE statements are rendered 

3661 based on those attributes in each state that have logged changes. 

3662 When False, all attributes present are rendered into the SET clause 

3663 with the exception of primary key attributes. 

3664 

3665 :param preserve_order: when True, the order of inserts and updates 

3666 matches exactly the order in which the objects are given. When 

3667 False, common types of objects are grouped into inserts 

3668 and updates, to allow for more batching opportunities. 

3669 

3670 .. versionadded:: 1.3 

3671 

3672 .. seealso:: 

3673 

3674 :ref:`bulk_operations` 

3675 

3676 :meth:`.Session.bulk_insert_mappings` 

3677 

3678 :meth:`.Session.bulk_update_mappings` 

3679 

3680 """ 

3681 

3682 obj_states = (attributes.instance_state(obj) for obj in objects) 

3683 

3684 if not preserve_order: 

3685 # the purpose of this sort is just so that common mappers 

3686 # and persistence states are grouped together, so that groupby 

3687 # will return a single group for a particular type of mapper. 

3688 # it's not trying to be deterministic beyond that. 

3689 obj_states = sorted( 

3690 obj_states, 

3691 key=lambda state: (id(state.mapper), state.key is not None), 

3692 ) 

3693 

3694 def grouping_key(state): 

3695 return (state.mapper, state.key is not None) 

3696 

3697 for (mapper, isupdate), states in itertools.groupby( 

3698 obj_states, grouping_key 

3699 ): 

3700 self._bulk_save_mappings( 

3701 mapper, 

3702 states, 

3703 isupdate, 

3704 True, 

3705 return_defaults, 

3706 update_changed_only, 

3707 False, 

3708 ) 

3709 

3710 def bulk_insert_mappings( 

3711 self, mapper, mappings, return_defaults=False, render_nulls=False 

3712 ): 

3713 """Perform a bulk insert of the given list of mapping dictionaries. 

3714 

3715 The bulk insert feature allows plain Python dictionaries to be used as 

3716 the source of simple INSERT operations which can be more easily 

3717 grouped together into higher performing "executemany" 

3718 operations. Using dictionaries, there is no "history" or session 

3719 state management features in use, reducing latency when inserting 

3720 large numbers of simple rows. 

3721 

3722 The values within the dictionaries as given are typically passed 

3723 without modification into Core :meth:`_expression.Insert` constructs, 

3724 after 

3725 organizing the values within them across the tables to which 

3726 the given mapper is mapped. 

3727 

3728 .. versionadded:: 1.0.0 

3729 

3730 .. legacy:: 

3731 

3732 The bulk insert feature allows for a lower-latency INSERT 

3733 of rows at the expense of most other unit-of-work features. 

3734 Features such as object management, relationship handling, 

3735 and SQL clause support are silently omitted in favor of raw 

3736 INSERT of records. 

3737 

3738 In SQLAlchemy 2.0, improved versions of the bulk insert/update 

3739 methods are introduced, with clearer behavior and 

3740 documentation, new capabilities, and much better performance. 

3741 

3742 For 1.4 use, **please read the list of caveats at** 

3743 :ref:`bulk_operations_caveats` **before using this method, and 

3744 fully test and confirm the functionality of all code developed 

3745 using these systems.** 

3746 

3747 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 

3748 object, 

3749 representing the single kind of object represented within the mapping 

3750 list. 

3751 

3752 :param mappings: a sequence of dictionaries, each one containing the 

3753 state of the mapped row to be inserted, in terms of the attribute 

3754 names on the mapped class. If the mapping refers to multiple tables, 

3755 such as a joined-inheritance mapping, each dictionary must contain all 

3756 keys to be populated into all tables. 

3757 

3758 :param return_defaults: when True, rows that are missing values which 

3759 generate defaults, namely integer primary key defaults and sequences, 

3760 will be inserted **one at a time**, so that the primary key value 

3761 is available. In particular this will allow joined-inheritance 

3762 and other multi-table mappings to insert correctly without the need 

3763 to provide primary 

3764 key values ahead of time; however, 

3765 :paramref:`.Session.bulk_insert_mappings.return_defaults` 

3766 **greatly reduces the performance gains** of the method overall. 

3767 If the rows 

3768 to be inserted only refer to a single table, then there is no 

3769 reason this flag should be set as the returned default information 

3770 is not used. 

3771 

3772 :param render_nulls: When True, a value of ``None`` will result 

3773 in a NULL value being included in the INSERT statement, rather 

3774 than the column being omitted from the INSERT. This allows all 

3775 the rows being INSERTed to have the identical set of columns which 

3776 allows the full set of rows to be batched to the DBAPI. Normally, 

3777 each column-set that contains a different combination of NULL values 

3778 than the previous row must omit a different series of columns from 

3779 the rendered INSERT statement, which means it must be emitted as a 

3780 separate statement. By passing this flag, the full set of rows 

3781 are guaranteed to be batchable into one batch; the cost however is 

3782 that server-side defaults which are invoked by an omitted column will 

3783 be skipped, so care must be taken to ensure that these are not 

3784 necessary. 

3785 

3786 .. warning:: 

3787 

3788 When this flag is set, **server side default SQL values will 

3789 not be invoked** for those columns that are inserted as NULL; 

3790 the NULL value will be sent explicitly. Care must be taken 

3791 to ensure that no server-side default functions need to be 

3792 invoked for the operation as a whole. 

3793 

3794 .. versionadded:: 1.1 

3795 

3796 .. seealso:: 

3797 

3798 :ref:`bulk_operations` 

3799 

3800 :meth:`.Session.bulk_save_objects` 

3801 

3802 :meth:`.Session.bulk_update_mappings` 

3803 

3804 """ 

3805 self._bulk_save_mappings( 

3806 mapper, 

3807 mappings, 

3808 False, 

3809 False, 

3810 return_defaults, 

3811 False, 

3812 render_nulls, 

3813 ) 

3814 

3815 def bulk_update_mappings(self, mapper, mappings): 

3816 """Perform a bulk update of the given list of mapping dictionaries. 

3817 

3818 The bulk update feature allows plain Python dictionaries to be used as 

3819 the source of simple UPDATE operations which can be more easily 

3820 grouped together into higher performing "executemany" 

3821 operations. Using dictionaries, there is no "history" or session 

3822 state management features in use, reducing latency when updating 

3823 large numbers of simple rows. 

3824 

3825 .. versionadded:: 1.0.0 

3826 

3827 .. legacy:: 

3828 

3829 The bulk update feature allows for a lower-latency UPDATE 

3830 of rows at the expense of most other unit-of-work features. 

3831 Features such as object management, relationship handling, 

3832 and SQL clause support are silently omitted in favor of raw 

3833 UPDATES of records. 

3834 

3835 In SQLAlchemy 2.0, improved versions of the bulk insert/update 

3836 methods are introduced, with clearer behavior and 

3837 documentation, new capabilities, and much better performance. 

3838 

3839 For 1.4 use, **please read the list of caveats at** 

3840 :ref:`bulk_operations_caveats` **before using this method, and 

3841 fully test and confirm the functionality of all code developed 

3842 using these systems.** 

3843 

3844 :param mapper: a mapped class, or the actual :class:`_orm.Mapper` 

3845 object, 

3846 representing the single kind of object represented within the mapping 

3847 list. 

3848 

3849 :param mappings: a sequence of dictionaries, each one containing the 

3850 state of the mapped row to be updated, in terms of the attribute names 

3851 on the mapped class. If the mapping refers to multiple tables, such 

3852 as a joined-inheritance mapping, each dictionary may contain keys 

3853 corresponding to all tables. All those keys which are present and 

3854 are not part of the primary key are applied to the SET clause of the 

3855 UPDATE statement; the primary key values, which are required, are 

3856 applied to the WHERE clause. 

3857 

3858 

3859 .. seealso:: 

3860 

3861 :ref:`bulk_operations` 

3862 

3863 :meth:`.Session.bulk_insert_mappings` 

3864 

3865 :meth:`.Session.bulk_save_objects` 

3866 

3867 """ 

3868 self._bulk_save_mappings( 

3869 mapper, mappings, True, False, False, False, False 

3870 ) 

3871 

3872 def _bulk_save_mappings( 

3873 self, 

3874 mapper, 

3875 mappings, 

3876 isupdate, 

3877 isstates, 

3878 return_defaults, 

3879 update_changed_only, 

3880 render_nulls, 

3881 ): 

3882 mapper = _class_to_mapper(mapper) 

3883 self._flushing = True 

3884 

3885 transaction = self.begin(_subtrans=True) 

3886 try: 

3887 if isupdate: 

3888 persistence._bulk_update( 

3889 mapper, 

3890 mappings, 

3891 transaction, 

3892 isstates, 

3893 update_changed_only, 

3894 ) 

3895 else: 

3896 persistence._bulk_insert( 

3897 mapper, 

3898 mappings, 

3899 transaction, 

3900 isstates, 

3901 return_defaults, 

3902 render_nulls, 

3903 ) 

3904 transaction.commit() 

3905 

3906 except: 

3907 with util.safe_reraise(): 

3908 transaction.rollback(_capture_exception=True) 

3909 finally: 

3910 self._flushing = False 

3911 

3912 def is_modified(self, instance, include_collections=True): 

3913 r"""Return ``True`` if the given instance has locally 

3914 modified attributes. 

3915 

3916 This method retrieves the history for each instrumented 

3917 attribute on the instance and performs a comparison of the current 

3918 value to its previously committed value, if any. 

3919 

3920 It is in effect a more expensive and accurate 

3921 version of checking for the given instance in the 

3922 :attr:`.Session.dirty` collection; a full test for 

3923 each attribute's net "dirty" status is performed. 

3924 

3925 E.g.:: 

3926 

3927 return session.is_modified(someobject) 

3928 

3929 A few caveats to this method apply: 

3930 

3931 * Instances present in the :attr:`.Session.dirty` collection may 

3932 report ``False`` when tested with this method. This is because 

3933 the object may have received change events via attribute mutation, 

3934 thus placing it in :attr:`.Session.dirty`, but ultimately the state 

3935 is the same as that loaded from the database, resulting in no net 

3936 change here. 

3937 * Scalar attributes may not have recorded the previously set 

3938 value when a new value was applied, if the attribute was not loaded, 

3939 or was expired, at the time the new value was received - in these 

3940 cases, the attribute is assumed to have a change, even if there is 

3941 ultimately no net change against its database value. SQLAlchemy in 

3942 most cases does not need the "old" value when a set event occurs, so 

3943 it skips the expense of a SQL call if the old value isn't present, 

3944 based on the assumption that an UPDATE of the scalar value is 

3945 usually needed, and in those few cases where it isn't, is less 

3946 expensive on average than issuing a defensive SELECT. 

3947 

3948 The "old" value is fetched unconditionally upon set only if the 

3949 attribute container has the ``active_history`` flag set to ``True``. 

3950 This flag is set typically for primary key attributes and scalar 

3951 object references that are not a simple many-to-one. To set this 

3952 flag for any arbitrary mapped column, use the ``active_history`` 

3953 argument with :func:`.column_property`. 

3954 

3955 :param instance: mapped instance to be tested for pending changes. 

3956 :param include_collections: Indicates if multivalued collections 

3957 should be included in the operation. Setting this to ``False`` is a 

3958 way to detect only local-column based properties (i.e. scalar columns 

3959 or many-to-one foreign keys) that would result in an UPDATE for this 

3960 instance upon flush. 

3961 

3962 """ 

3963 state = object_state(instance) 

3964 

3965 if not state.modified: 

3966 return False 

3967 

3968 dict_ = state.dict 

3969 

3970 for attr in state.manager.attributes: 

3971 if ( 

3972 not include_collections 

3973 and hasattr(attr.impl, "get_collection") 

3974 ) or not hasattr(attr.impl, "get_history"): 

3975 continue 

3976 

3977 (added, unchanged, deleted) = attr.impl.get_history( 

3978 state, dict_, passive=attributes.NO_CHANGE 

3979 ) 

3980 

3981 if added or deleted: 

3982 return True 

3983 else: 

3984 return False 

3985 

3986 @property 

3987 def is_active(self): 

3988 """True if this :class:`.Session` not in "partial rollback" state. 

3989 

3990 .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins 

3991 a new transaction immediately, so this attribute will be False 

3992 when the :class:`_orm.Session` is first instantiated. 

3993 

3994 "partial rollback" state typically indicates that the flush process 

3995 of the :class:`_orm.Session` has failed, and that the 

3996 :meth:`_orm.Session.rollback` method must be emitted in order to 

3997 fully roll back the transaction. 

3998 

3999 If this :class:`_orm.Session` is not in a transaction at all, the 

4000 :class:`_orm.Session` will autobegin when it is first used, so in this 

4001 case :attr:`_orm.Session.is_active` will return True. 

4002 

4003 Otherwise, if this :class:`_orm.Session` is within a transaction, 

4004 and that transaction has not been rolled back internally, the 

4005 :attr:`_orm.Session.is_active` will also return True. 

4006 

4007 .. seealso:: 

4008 

4009 :ref:`faq_session_rollback` 

4010 

4011 :meth:`_orm.Session.in_transaction` 

4012 

4013 """ 

4014 if self.autocommit: 

4015 return ( 

4016 self._transaction is not None and self._transaction.is_active 

4017 ) 

4018 else: 

4019 return self._transaction is None or self._transaction.is_active 

4020 

4021 identity_map = None 

4022 """A mapping of object identities to objects themselves. 

4023 

4024 Iterating through ``Session.identity_map.values()`` provides 

4025 access to the full set of persistent objects (i.e., those 

4026 that have row identity) currently in the session. 

4027 

4028 .. seealso:: 

4029 

4030 :func:`.identity_key` - helper function to produce the keys used 

4031 in this dictionary. 

4032 

4033 """ 

4034 

4035 @property 

4036 def _dirty_states(self): 

4037 """The set of all persistent states considered dirty. 

4038 

4039 This method returns all states that were modified including 

4040 those that were possibly deleted. 

4041 

4042 """ 

4043 return self.identity_map._dirty_states() 

4044 

4045 @property 

4046 def dirty(self): 

4047 """The set of all persistent instances considered dirty. 

4048 

4049 E.g.:: 

4050 

4051 some_mapped_object in session.dirty 

4052 

4053 Instances are considered dirty when they were modified but not 

4054 deleted. 

4055 

4056 Note that this 'dirty' calculation is 'optimistic'; most 

4057 attribute-setting or collection modification operations will 

4058 mark an instance as 'dirty' and place it in this set, even if 

4059 there is no net change to the attribute's value. At flush 

4060 time, the value of each attribute is compared to its 

4061 previously saved value, and if there's no net change, no SQL 

4062 operation will occur (this is a more expensive operation so 

4063 it's only done at flush time). 

4064 

4065 To check if an instance has actionable net changes to its 

4066 attributes, use the :meth:`.Session.is_modified` method. 

4067 

4068 """ 

4069 return util.IdentitySet( 

4070 [ 

4071 state.obj() 

4072 for state in self._dirty_states 

4073 if state not in self._deleted 

4074 ] 

4075 ) 

4076 

4077 @property 

4078 def deleted(self): 

4079 "The set of all instances marked as 'deleted' within this ``Session``" 

4080 

4081 return util.IdentitySet(list(self._deleted.values())) 

4082 

4083 @property 

4084 def new(self): 

4085 "The set of all instances marked as 'new' within this ``Session``." 

4086 

4087 return util.IdentitySet(list(self._new.values())) 

4088 

4089 

4090class sessionmaker(_SessionClassMethods): 

4091 """A configurable :class:`.Session` factory. 

4092 

4093 The :class:`.sessionmaker` factory generates new 

4094 :class:`.Session` objects when called, creating them given 

4095 the configurational arguments established here. 

4096 

4097 e.g.:: 

4098 

4099 from sqlalchemy import create_engine 

4100 from sqlalchemy.orm import sessionmaker 

4101 

4102 # an Engine, which the Session will use for connection 

4103 # resources 

4104 engine = create_engine('postgresql://scott:tiger@localhost/') 

4105 

4106 Session = sessionmaker(engine) 

4107 

4108 with Session() as session: 

4109 session.add(some_object) 

4110 session.add(some_other_object) 

4111 session.commit() 

4112 

4113 Context manager use is optional; otherwise, the returned 

4114 :class:`_orm.Session` object may be closed explicitly via the 

4115 :meth:`_orm.Session.close` method. Using a 

4116 ``try:/finally:`` block is optional, however will ensure that the close 

4117 takes place even if there are database errors:: 

4118 

4119 session = Session() 

4120 try: 

4121 session.add(some_object) 

4122 session.add(some_other_object) 

4123 session.commit() 

4124 finally: 

4125 session.close() 

4126 

4127 :class:`.sessionmaker` acts as a factory for :class:`_orm.Session` 

4128 objects in the same way as an :class:`_engine.Engine` acts as a factory 

4129 for :class:`_engine.Connection` objects. In this way it also includes 

4130 a :meth:`_orm.sessionmaker.begin` method, that provides a context 

4131 manager which both begins and commits a transaction, as well as closes 

4132 out the :class:`_orm.Session` when complete, rolling back the transaction 

4133 if any errors occur:: 

4134 

4135 Session = sessionmaker(engine) 

4136 

4137 with Session.begin() as session: 

4138 session.add(some_object) 

4139 session.add(some_other_object) 

4140 # commits transaction, closes session 

4141 

4142 .. versionadded:: 1.4 

4143 

4144 When calling upon :class:`_orm.sessionmaker` to construct a 

4145 :class:`_orm.Session`, keyword arguments may also be passed to the 

4146 method; these arguments will override that of the globally configured 

4147 parameters. Below we use a :class:`_orm.sessionmaker` bound to a certain 

4148 :class:`_engine.Engine` to produce a :class:`_orm.Session` that is instead 

4149 bound to a specific :class:`_engine.Connection` procured from that engine:: 

4150 

4151 Session = sessionmaker(engine) 

4152 

4153 # bind an individual session to a connection 

4154 

4155 with engine.connect() as connection: 

4156 with Session(bind=connection) as session: 

4157 # work with session 

4158 

4159 The class also includes a method :meth:`_orm.sessionmaker.configure`, which 

4160 can be used to specify additional keyword arguments to the factory, which 

4161 will take effect for subsequent :class:`.Session` objects generated. This 

4162 is usually used to associate one or more :class:`_engine.Engine` objects 

4163 with an existing 

4164 :class:`.sessionmaker` factory before it is first used:: 

4165 

4166 # application starts, sessionmaker does not have 

4167 # an engine bound yet 

4168 Session = sessionmaker() 

4169 

4170 # ... later, when an engine URL is read from a configuration 

4171 # file or other events allow the engine to be created 

4172 engine = create_engine('sqlite:///foo.db') 

4173 Session.configure(bind=engine) 

4174 

4175 sess = Session() 

4176 # work with session 

4177 

4178 .. seealso:: 

4179 

4180 :ref:`session_getting` - introductory text on creating 

4181 sessions using :class:`.sessionmaker`. 

4182 

4183 """ 

4184 

4185 def __init__( 

4186 self, 

4187 bind=None, 

4188 class_=Session, 

4189 autoflush=True, 

4190 autocommit=False, 

4191 expire_on_commit=True, 

4192 info=None, 

4193 **kw 

4194 ): 

4195 r"""Construct a new :class:`.sessionmaker`. 

4196 

4197 All arguments here except for ``class_`` correspond to arguments 

4198 accepted by :class:`.Session` directly. See the 

4199 :meth:`.Session.__init__` docstring for more details on parameters. 

4200 

4201 :param bind: a :class:`_engine.Engine` or other :class:`.Connectable` 

4202 with 

4203 which newly created :class:`.Session` objects will be associated. 

4204 :param class\_: class to use in order to create new :class:`.Session` 

4205 objects. Defaults to :class:`.Session`. 

4206 :param autoflush: The autoflush setting to use with newly created 

4207 :class:`.Session` objects. 

4208 :param autocommit: The autocommit setting to use with newly created 

4209 :class:`.Session` objects. 

4210 :param expire_on_commit=True: the 

4211 :paramref:`_orm.Session.expire_on_commit` setting to use 

4212 with newly created :class:`.Session` objects. 

4213 

4214 :param info: optional dictionary of information that will be available 

4215 via :attr:`.Session.info`. Note this dictionary is *updated*, not 

4216 replaced, when the ``info`` parameter is specified to the specific 

4217 :class:`.Session` construction operation. 

4218 

4219 :param \**kw: all other keyword arguments are passed to the 

4220 constructor of newly created :class:`.Session` objects. 

4221 

4222 """ 

4223 kw["bind"] = bind 

4224 kw["autoflush"] = autoflush 

4225 kw["autocommit"] = autocommit 

4226 kw["expire_on_commit"] = expire_on_commit 

4227 if info is not None: 

4228 kw["info"] = info 

4229 self.kw = kw 

4230 # make our own subclass of the given class, so that 

4231 # events can be associated with it specifically. 

4232 self.class_ = type(class_.__name__, (class_,), {}) 

4233 

4234 def begin(self): 

4235 """Produce a context manager that both provides a new 

4236 :class:`_orm.Session` as well as a transaction that commits. 

4237 

4238 

4239 e.g.:: 

4240 

4241 Session = sessionmaker(some_engine) 

4242 

4243 with Session.begin() as session: 

4244 session.add(some_object) 

4245 

4246 # commits transaction, closes session 

4247 

4248 .. versionadded:: 1.4 

4249 

4250 

4251 """ 

4252 

4253 session = self() 

4254 return session._maker_context_manager() 

4255 

4256 def __call__(self, **local_kw): 

4257 """Produce a new :class:`.Session` object using the configuration 

4258 established in this :class:`.sessionmaker`. 

4259 

4260 In Python, the ``__call__`` method is invoked on an object when 

4261 it is "called" in the same way as a function:: 

4262 

4263 Session = sessionmaker() 

4264 session = Session() # invokes sessionmaker.__call__() 

4265 

4266 """ 

4267 for k, v in self.kw.items(): 

4268 if k == "info" and "info" in local_kw: 

4269 d = v.copy() 

4270 d.update(local_kw["info"]) 

4271 local_kw["info"] = d 

4272 else: 

4273 local_kw.setdefault(k, v) 

4274 return self.class_(**local_kw) 

4275 

4276 def configure(self, **new_kw): 

4277 """(Re)configure the arguments for this sessionmaker. 

4278 

4279 e.g.:: 

4280 

4281 Session = sessionmaker() 

4282 

4283 Session.configure(bind=create_engine('sqlite://')) 

4284 """ 

4285 self.kw.update(new_kw) 

4286 

4287 def __repr__(self): 

4288 return "%s(class_=%r, %s)" % ( 

4289 self.__class__.__name__, 

4290 self.class_.__name__, 

4291 ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), 

4292 ) 

4293 

4294 

4295def close_all_sessions(): 

4296 """Close all sessions in memory. 

4297 

4298 This function consults a global registry of all :class:`.Session` objects 

4299 and calls :meth:`.Session.close` on them, which resets them to a clean 

4300 state. 

4301 

4302 This function is not for general use but may be useful for test suites 

4303 within the teardown scheme. 

4304 

4305 .. versionadded:: 1.3 

4306 

4307 """ 

4308 

4309 for sess in _sessions.values(): 

4310 sess.close() 

4311 

4312 

4313def make_transient(instance): 

4314 """Alter the state of the given instance so that it is :term:`transient`. 

4315 

4316 .. note:: 

4317 

4318 :func:`.make_transient` is a special-case function for 

4319 advanced use cases only. 

4320 

4321 The given mapped instance is assumed to be in the :term:`persistent` or 

4322 :term:`detached` state. The function will remove its association with any 

4323 :class:`.Session` as well as its :attr:`.InstanceState.identity`. The 

4324 effect is that the object will behave as though it were newly constructed, 

4325 except retaining any attribute / collection values that were loaded at the 

4326 time of the call. The :attr:`.InstanceState.deleted` flag is also reset 

4327 if this object had been deleted as a result of using 

4328 :meth:`.Session.delete`. 

4329 

4330 .. warning:: 

4331 

4332 :func:`.make_transient` does **not** "unexpire" or otherwise eagerly 

4333 load ORM-mapped attributes that are not currently loaded at the time 

4334 the function is called. This includes attributes which: 

4335 

4336 * were expired via :meth:`.Session.expire` 

4337 

4338 * were expired as the natural effect of committing a session 

4339 transaction, e.g. :meth:`.Session.commit` 

4340 

4341 * are normally :term:`lazy loaded` but are not currently loaded 

4342 

4343 * are "deferred" via :ref:`deferred` and are not yet loaded 

4344 

4345 * were not present in the query which loaded this object, such as that 

4346 which is common in joined table inheritance and other scenarios. 

4347 

4348 After :func:`.make_transient` is called, unloaded attributes such 

4349 as those above will normally resolve to the value ``None`` when 

4350 accessed, or an empty collection for a collection-oriented attribute. 

4351 As the object is transient and un-associated with any database 

4352 identity, it will no longer retrieve these values. 

4353 

4354 .. seealso:: 

4355 

4356 :func:`.make_transient_to_detached` 

4357 

4358 """ 

4359 state = attributes.instance_state(instance) 

4360 s = _state_session(state) 

4361 if s: 

4362 s._expunge_states([state]) 

4363 

4364 # remove expired state 

4365 state.expired_attributes.clear() 

4366 

4367 # remove deferred callables 

4368 if state.callables: 

4369 del state.callables 

4370 

4371 if state.key: 

4372 del state.key 

4373 if state._deleted: 

4374 del state._deleted 

4375 

4376 

4377def make_transient_to_detached(instance): 

4378 """Make the given transient instance :term:`detached`. 

4379 

4380 .. note:: 

4381 

4382 :func:`.make_transient_to_detached` is a special-case function for 

4383 advanced use cases only. 

4384 

4385 All attribute history on the given instance 

4386 will be reset as though the instance were freshly loaded 

4387 from a query. Missing attributes will be marked as expired. 

4388 The primary key attributes of the object, which are required, will be made 

4389 into the "key" of the instance. 

4390 

4391 The object can then be added to a session, or merged 

4392 possibly with the load=False flag, at which point it will look 

4393 as if it were loaded that way, without emitting SQL. 

4394 

4395 This is a special use case function that differs from a normal 

4396 call to :meth:`.Session.merge` in that a given persistent state 

4397 can be manufactured without any SQL calls. 

4398 

4399 .. seealso:: 

4400 

4401 :func:`.make_transient` 

4402 

4403 :meth:`.Session.enable_relationship_loading` 

4404 

4405 """ 

4406 state = attributes.instance_state(instance) 

4407 if state.session_id or state.key: 

4408 raise sa_exc.InvalidRequestError("Given object must be transient") 

4409 state.key = state.mapper._identity_key_from_state(state) 

4410 if state._deleted: 

4411 del state._deleted 

4412 state._commit_all(state.dict) 

4413 state._expire_attributes(state.dict, state.unloaded_expirable) 

4414 

4415 

4416def object_session(instance): 

4417 """Return the :class:`.Session` to which the given instance belongs. 

4418 

4419 This is essentially the same as the :attr:`.InstanceState.session` 

4420 accessor. See that attribute for details. 

4421 

4422 """ 

4423 

4424 try: 

4425 state = attributes.instance_state(instance) 

4426 except exc.NO_STATE as err: 

4427 util.raise_( 

4428 exc.UnmappedInstanceError(instance), 

4429 replace_context=err, 

4430 ) 

4431 else: 

4432 return _state_session(state) 

4433 

4434 

4435_new_sessionid = util.counter()