1# orm/scoping.py
2# Copyright (C) 2005-2025 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
8from __future__ import annotations
9
10from typing import Any
11from typing import Callable
12from typing import Dict
13from typing import Generic
14from typing import Iterable
15from typing import Iterator
16from typing import Optional
17from typing import overload
18from typing import Protocol
19from typing import Sequence
20from typing import Tuple
21from typing import Type
22from typing import TYPE_CHECKING
23from typing import TypeVar
24from typing import Union
25
26from .session import _S
27from .session import Session
28from .. import exc as sa_exc
29from .. import util
30from ..util import create_proxy_methods
31from ..util import ScopedRegistry
32from ..util import ThreadLocalRegistry
33from ..util import warn
34from ..util import warn_deprecated
35from ..util.typing import TupleAny
36from ..util.typing import TypeVarTuple
37from ..util.typing import Unpack
38
39if TYPE_CHECKING:
40 from ._typing import _EntityType
41 from ._typing import _IdentityKeyType
42 from ._typing import OrmExecuteOptionsParameter
43 from .identity import IdentityMap
44 from .interfaces import ORMOption
45 from .mapper import Mapper
46 from .query import Query
47 from .query import RowReturningQuery
48 from .session import _BindArguments
49 from .session import _EntityBindKey
50 from .session import _PKIdentityArgument
51 from .session import _SessionBind
52 from .session import sessionmaker
53 from .session import SessionTransaction
54 from ..engine import Connection
55 from ..engine import CursorResult
56 from ..engine import Engine
57 from ..engine import Result
58 from ..engine import Row
59 from ..engine import RowMapping
60 from ..engine.interfaces import _CoreAnyExecuteParams
61 from ..engine.interfaces import _CoreSingleExecuteParams
62 from ..engine.interfaces import CoreExecuteOptionsParameter
63 from ..engine.result import ScalarResult
64 from ..sql._typing import _ColumnsClauseArgument
65 from ..sql._typing import _T0
66 from ..sql._typing import _T1
67 from ..sql._typing import _T2
68 from ..sql._typing import _T3
69 from ..sql._typing import _T4
70 from ..sql._typing import _T5
71 from ..sql._typing import _T6
72 from ..sql._typing import _T7
73 from ..sql._typing import _TypedColumnClauseArgument as _TCCA
74 from ..sql.base import Executable
75 from ..sql.dml import UpdateBase
76 from ..sql.elements import ClauseElement
77 from ..sql.roles import TypedColumnsClauseRole
78 from ..sql.selectable import ForUpdateParameter
79 from ..sql.selectable import TypedReturnsRows
80
81
82_T = TypeVar("_T", bound=Any)
83_Ts = TypeVarTuple("_Ts")
84
85
86class QueryPropertyDescriptor(Protocol):
87 """Describes the type applied to a class-level
88 :meth:`_orm.scoped_session.query_property` attribute.
89
90 .. versionadded:: 2.0.5
91
92 """
93
94 def __get__(self, instance: Any, owner: Type[_T]) -> Query[_T]: ...
95
96
97_O = TypeVar("_O", bound=object)
98
99__all__ = ["scoped_session"]
100
101
102@create_proxy_methods(
103 Session,
104 ":class:`_orm.Session`",
105 ":class:`_orm.scoping.scoped_session`",
106 classmethods=["object_session", "identity_key"],
107 methods=[
108 "__contains__",
109 "__iter__",
110 "add",
111 "add_all",
112 "begin",
113 "begin_nested",
114 "close",
115 "reset",
116 "commit",
117 "connection",
118 "delete",
119 "delete_all",
120 "execute",
121 "expire",
122 "expire_all",
123 "expunge",
124 "expunge_all",
125 "flush",
126 "get",
127 "get_one",
128 "get_bind",
129 "is_modified",
130 "bulk_save_objects",
131 "bulk_insert_mappings",
132 "bulk_update_mappings",
133 "merge",
134 "merge_all",
135 "query",
136 "refresh",
137 "rollback",
138 "scalar",
139 "scalars",
140 ],
141 attributes=[
142 "bind",
143 "dirty",
144 "deleted",
145 "new",
146 "identity_map",
147 "is_active",
148 "autoflush",
149 "no_autoflush",
150 "info",
151 ],
152)
153class scoped_session(Generic[_S]):
154 """Provides scoped management of :class:`.Session` objects.
155
156 See :ref:`unitofwork_contextual` for a tutorial.
157
158 .. note::
159
160 When using :ref:`asyncio_toplevel`, the async-compatible
161 :class:`_asyncio.async_scoped_session` class should be
162 used in place of :class:`.scoped_session`.
163
164 """
165
166 _support_async: bool = False
167
168 session_factory: sessionmaker[_S]
169 """The `session_factory` provided to `__init__` is stored in this
170 attribute and may be accessed at a later time. This can be useful when
171 a new non-scoped :class:`.Session` is needed."""
172
173 registry: ScopedRegistry[_S]
174
175 def __init__(
176 self,
177 session_factory: sessionmaker[_S],
178 scopefunc: Optional[Callable[[], Any]] = None,
179 ):
180 """Construct a new :class:`.scoped_session`.
181
182 :param session_factory: a factory to create new :class:`.Session`
183 instances. This is usually, but not necessarily, an instance
184 of :class:`.sessionmaker`.
185 :param scopefunc: optional function which defines
186 the current scope. If not passed, the :class:`.scoped_session`
187 object assumes "thread-local" scope, and will use
188 a Python ``threading.local()`` in order to maintain the current
189 :class:`.Session`. If passed, the function should return
190 a hashable token; this token will be used as the key in a
191 dictionary in order to store and retrieve the current
192 :class:`.Session`.
193
194 """
195 self.session_factory = session_factory
196
197 if scopefunc:
198 self.registry = ScopedRegistry(session_factory, scopefunc)
199 else:
200 self.registry = ThreadLocalRegistry(session_factory)
201
202 @property
203 def _proxied(self) -> _S:
204 return self.registry()
205
206 def __call__(self, **kw: Any) -> _S:
207 r"""Return the current :class:`.Session`, creating it
208 using the :attr:`.scoped_session.session_factory` if not present.
209
210 :param \**kw: Keyword arguments will be passed to the
211 :attr:`.scoped_session.session_factory` callable, if an existing
212 :class:`.Session` is not present. If the :class:`.Session` is present
213 and keyword arguments have been passed,
214 :exc:`~sqlalchemy.exc.InvalidRequestError` is raised.
215
216 """
217 if kw:
218 if self.registry.has():
219 raise sa_exc.InvalidRequestError(
220 "Scoped session is already present; "
221 "no new arguments may be specified."
222 )
223 else:
224 sess = self.session_factory(**kw)
225 self.registry.set(sess)
226 else:
227 sess = self.registry()
228 if not self._support_async and sess._is_asyncio:
229 warn_deprecated(
230 "Using `scoped_session` with asyncio is deprecated and "
231 "will raise an error in a future version. "
232 "Please use `async_scoped_session` instead.",
233 "1.4.23",
234 )
235 return sess
236
237 def configure(self, **kwargs: Any) -> None:
238 """reconfigure the :class:`.sessionmaker` used by this
239 :class:`.scoped_session`.
240
241 See :meth:`.sessionmaker.configure`.
242
243 """
244
245 if self.registry.has():
246 warn(
247 "At least one scoped session is already present. "
248 " configure() can not affect sessions that have "
249 "already been created."
250 )
251
252 self.session_factory.configure(**kwargs)
253
254 def remove(self) -> None:
255 """Dispose of the current :class:`.Session`, if present.
256
257 This will first call :meth:`.Session.close` method
258 on the current :class:`.Session`, which releases any existing
259 transactional/connection resources still being held; transactions
260 specifically are rolled back. The :class:`.Session` is then
261 discarded. Upon next usage within the same scope,
262 the :class:`.scoped_session` will produce a new
263 :class:`.Session` object.
264
265 """
266
267 if self.registry.has():
268 self.registry().close()
269 self.registry.clear()
270
271 def query_property(
272 self, query_cls: Optional[Type[Query[_T]]] = None
273 ) -> QueryPropertyDescriptor:
274 """return a class property which produces a legacy
275 :class:`_query.Query` object against the class and the current
276 :class:`.Session` when called.
277
278 .. legacy:: The :meth:`_orm.scoped_session.query_property` accessor
279 is specific to the legacy :class:`.Query` object and is not
280 considered to be part of :term:`2.0-style` ORM use.
281
282 e.g.::
283
284 from sqlalchemy.orm import QueryPropertyDescriptor
285 from sqlalchemy.orm import scoped_session
286 from sqlalchemy.orm import sessionmaker
287
288 Session = scoped_session(sessionmaker())
289
290
291 class MyClass:
292 query: QueryPropertyDescriptor = Session.query_property()
293
294
295 # after mappers are defined
296 result = MyClass.query.filter(MyClass.name == "foo").all()
297
298 Produces instances of the session's configured query class by
299 default. To override and use a custom implementation, provide
300 a ``query_cls`` callable. The callable will be invoked with
301 the class's mapper as a positional argument and a session
302 keyword argument.
303
304 There is no limit to the number of query properties placed on
305 a class.
306
307 """
308
309 class query:
310 def __get__(s, instance: Any, owner: Type[_O]) -> Query[_O]:
311 if query_cls:
312 # custom query class
313 return query_cls(owner, session=self.registry()) # type: ignore # noqa: E501
314 else:
315 # session's configured query class
316 return self.registry().query(owner)
317
318 return query()
319
320 # START PROXY METHODS scoped_session
321
322 # code within this block is **programmatically,
323 # statically generated** by tools/generate_proxy_methods.py
324
325 def __contains__(self, instance: object) -> bool:
326 r"""Return True if the instance is associated with this session.
327
328 .. container:: class_bases
329
330 Proxied for the :class:`_orm.Session` class on
331 behalf of the :class:`_orm.scoping.scoped_session` class.
332
333 The instance may be pending or persistent within the Session for a
334 result of True.
335
336
337 """ # noqa: E501
338
339 return self._proxied.__contains__(instance)
340
341 def __iter__(self) -> Iterator[object]:
342 r"""Iterate over all pending or persistent instances within this
343 Session.
344
345 .. container:: class_bases
346
347 Proxied for the :class:`_orm.Session` class on
348 behalf of the :class:`_orm.scoping.scoped_session` class.
349
350
351 """ # noqa: E501
352
353 return self._proxied.__iter__()
354
355 def add(self, instance: object, *, _warn: bool = True) -> None:
356 r"""Place an object into this :class:`_orm.Session`.
357
358 .. container:: class_bases
359
360 Proxied for the :class:`_orm.Session` class on
361 behalf of the :class:`_orm.scoping.scoped_session` class.
362
363 Objects that are in the :term:`transient` state when passed to the
364 :meth:`_orm.Session.add` method will move to the
365 :term:`pending` state, until the next flush, at which point they
366 will move to the :term:`persistent` state.
367
368 Objects that are in the :term:`detached` state when passed to the
369 :meth:`_orm.Session.add` method will move to the :term:`persistent`
370 state directly.
371
372 If the transaction used by the :class:`_orm.Session` is rolled back,
373 objects which were transient when they were passed to
374 :meth:`_orm.Session.add` will be moved back to the
375 :term:`transient` state, and will no longer be present within this
376 :class:`_orm.Session`.
377
378 .. seealso::
379
380 :meth:`_orm.Session.add_all`
381
382 :ref:`session_adding` - at :ref:`session_basics`
383
384
385 """ # noqa: E501
386
387 return self._proxied.add(instance, _warn=_warn)
388
389 def add_all(self, instances: Iterable[object]) -> None:
390 r"""Add the given collection of instances to this :class:`_orm.Session`.
391
392 .. container:: class_bases
393
394 Proxied for the :class:`_orm.Session` class on
395 behalf of the :class:`_orm.scoping.scoped_session` class.
396
397 See the documentation for :meth:`_orm.Session.add` for a general
398 behavioral description.
399
400 .. seealso::
401
402 :meth:`_orm.Session.add`
403
404 :ref:`session_adding` - at :ref:`session_basics`
405
406
407 """ # noqa: E501
408
409 return self._proxied.add_all(instances)
410
411 def begin(self, nested: bool = False) -> SessionTransaction:
412 r"""Begin a transaction, or nested transaction,
413 on this :class:`.Session`, if one is not already begun.
414
415 .. container:: class_bases
416
417 Proxied for the :class:`_orm.Session` class on
418 behalf of the :class:`_orm.scoping.scoped_session` class.
419
420 The :class:`_orm.Session` object features **autobegin** behavior,
421 so that normally it is not necessary to call the
422 :meth:`_orm.Session.begin`
423 method explicitly. However, it may be used in order to control
424 the scope of when the transactional state is begun.
425
426 When used to begin the outermost transaction, an error is raised
427 if this :class:`.Session` is already inside of a transaction.
428
429 :param nested: if True, begins a SAVEPOINT transaction and is
430 equivalent to calling :meth:`~.Session.begin_nested`. For
431 documentation on SAVEPOINT transactions, please see
432 :ref:`session_begin_nested`.
433
434 :return: the :class:`.SessionTransaction` object. Note that
435 :class:`.SessionTransaction`
436 acts as a Python context manager, allowing :meth:`.Session.begin`
437 to be used in a "with" block. See :ref:`session_explicit_begin` for
438 an example.
439
440 .. seealso::
441
442 :ref:`session_autobegin`
443
444 :ref:`unitofwork_transaction`
445
446 :meth:`.Session.begin_nested`
447
448
449
450 """ # noqa: E501
451
452 return self._proxied.begin(nested=nested)
453
454 def begin_nested(self) -> SessionTransaction:
455 r"""Begin a "nested" transaction on this Session, e.g. SAVEPOINT.
456
457 .. container:: class_bases
458
459 Proxied for the :class:`_orm.Session` class on
460 behalf of the :class:`_orm.scoping.scoped_session` class.
461
462 The target database(s) and associated drivers must support SQL
463 SAVEPOINT for this method to function correctly.
464
465 For documentation on SAVEPOINT
466 transactions, please see :ref:`session_begin_nested`.
467
468 :return: the :class:`.SessionTransaction` object. Note that
469 :class:`.SessionTransaction` acts as a context manager, allowing
470 :meth:`.Session.begin_nested` to be used in a "with" block.
471 See :ref:`session_begin_nested` for a usage example.
472
473 .. seealso::
474
475 :ref:`session_begin_nested`
476
477 :ref:`pysqlite_serializable` - special workarounds required
478 with the SQLite driver in order for SAVEPOINT to work
479 correctly. For asyncio use cases, see the section
480 :ref:`aiosqlite_serializable`.
481
482
483 """ # noqa: E501
484
485 return self._proxied.begin_nested()
486
487 def close(self) -> None:
488 r"""Close out the transactional resources and ORM objects used by this
489 :class:`_orm.Session`.
490
491 .. container:: class_bases
492
493 Proxied for the :class:`_orm.Session` class on
494 behalf of the :class:`_orm.scoping.scoped_session` class.
495
496 This expunges all ORM objects associated with this
497 :class:`_orm.Session`, ends any transaction in progress and
498 :term:`releases` any :class:`_engine.Connection` objects which this
499 :class:`_orm.Session` itself has checked out from associated
500 :class:`_engine.Engine` objects. The operation then leaves the
501 :class:`_orm.Session` in a state which it may be used again.
502
503 .. tip::
504
505 In the default running mode the :meth:`_orm.Session.close`
506 method **does not prevent the Session from being used again**.
507 The :class:`_orm.Session` itself does not actually have a
508 distinct "closed" state; it merely means
509 the :class:`_orm.Session` will release all database connections
510 and ORM objects.
511
512 Setting the parameter :paramref:`_orm.Session.close_resets_only`
513 to ``False`` will instead make the ``close`` final, meaning that
514 any further action on the session will be forbidden.
515
516 .. versionchanged:: 1.4 The :meth:`.Session.close` method does not
517 immediately create a new :class:`.SessionTransaction` object;
518 instead, the new :class:`.SessionTransaction` is created only if
519 the :class:`.Session` is used again for a database operation.
520
521 .. seealso::
522
523 :ref:`session_closing` - detail on the semantics of
524 :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`.
525
526 :meth:`_orm.Session.reset` - a similar method that behaves like
527 ``close()`` with the parameter
528 :paramref:`_orm.Session.close_resets_only` set to ``True``.
529
530
531 """ # noqa: E501
532
533 return self._proxied.close()
534
535 def reset(self) -> None:
536 r"""Close out the transactional resources and ORM objects used by this
537 :class:`_orm.Session`, resetting the session to its initial state.
538
539 .. container:: class_bases
540
541 Proxied for the :class:`_orm.Session` class on
542 behalf of the :class:`_orm.scoping.scoped_session` class.
543
544 This method provides for same "reset-only" behavior that the
545 :meth:`_orm.Session.close` method has provided historically, where the
546 state of the :class:`_orm.Session` is reset as though the object were
547 brand new, and ready to be used again.
548 This method may then be useful for :class:`_orm.Session` objects
549 which set :paramref:`_orm.Session.close_resets_only` to ``False``,
550 so that "reset only" behavior is still available.
551
552 .. versionadded:: 2.0.22
553
554 .. seealso::
555
556 :ref:`session_closing` - detail on the semantics of
557 :meth:`_orm.Session.close` and :meth:`_orm.Session.reset`.
558
559 :meth:`_orm.Session.close` - a similar method will additionally
560 prevent re-use of the Session when the parameter
561 :paramref:`_orm.Session.close_resets_only` is set to ``False``.
562
563 """ # noqa: E501
564
565 return self._proxied.reset()
566
567 def commit(self) -> None:
568 r"""Flush pending changes and commit the current transaction.
569
570 .. container:: class_bases
571
572 Proxied for the :class:`_orm.Session` class on
573 behalf of the :class:`_orm.scoping.scoped_session` class.
574
575 When the COMMIT operation is complete, all objects are fully
576 :term:`expired`, erasing their internal contents, which will be
577 automatically re-loaded when the objects are next accessed. In the
578 interim, these objects are in an expired state and will not function if
579 they are :term:`detached` from the :class:`.Session`. Additionally,
580 this re-load operation is not supported when using asyncio-oriented
581 APIs. The :paramref:`.Session.expire_on_commit` parameter may be used
582 to disable this behavior.
583
584 When there is no transaction in place for the :class:`.Session`,
585 indicating that no operations were invoked on this :class:`.Session`
586 since the previous call to :meth:`.Session.commit`, the method will
587 begin and commit an internal-only "logical" transaction, that does not
588 normally affect the database unless pending flush changes were
589 detected, but will still invoke event handlers and object expiration
590 rules.
591
592 The outermost database transaction is committed unconditionally,
593 automatically releasing any SAVEPOINTs in effect.
594
595 .. seealso::
596
597 :ref:`session_committing`
598
599 :ref:`unitofwork_transaction`
600
601 :ref:`asyncio_orm_avoid_lazyloads`
602
603
604 """ # noqa: E501
605
606 return self._proxied.commit()
607
608 def connection(
609 self,
610 bind_arguments: Optional[_BindArguments] = None,
611 execution_options: Optional[CoreExecuteOptionsParameter] = None,
612 ) -> Connection:
613 r"""Return a :class:`_engine.Connection` object corresponding to this
614 :class:`.Session` object's transactional state.
615
616 .. container:: class_bases
617
618 Proxied for the :class:`_orm.Session` class on
619 behalf of the :class:`_orm.scoping.scoped_session` class.
620
621 Either the :class:`_engine.Connection` corresponding to the current
622 transaction is returned, or if no transaction is in progress, a new
623 one is begun and the :class:`_engine.Connection`
624 returned (note that no
625 transactional state is established with the DBAPI until the first
626 SQL statement is emitted).
627
628 Ambiguity in multi-bind or unbound :class:`.Session` objects can be
629 resolved through any of the optional keyword arguments. This
630 ultimately makes usage of the :meth:`.get_bind` method for resolution.
631
632 :param bind_arguments: dictionary of bind arguments. May include
633 "mapper", "bind", "clause", other custom arguments that are passed
634 to :meth:`.Session.get_bind`.
635
636 :param execution_options: a dictionary of execution options that will
637 be passed to :meth:`_engine.Connection.execution_options`, **when the
638 connection is first procured only**. If the connection is already
639 present within the :class:`.Session`, a warning is emitted and
640 the arguments are ignored.
641
642 .. seealso::
643
644 :ref:`session_transaction_isolation`
645
646
647 """ # noqa: E501
648
649 return self._proxied.connection(
650 bind_arguments=bind_arguments, execution_options=execution_options
651 )
652
653 def delete(self, instance: object) -> None:
654 r"""Mark an instance as deleted.
655
656 .. container:: class_bases
657
658 Proxied for the :class:`_orm.Session` class on
659 behalf of the :class:`_orm.scoping.scoped_session` class.
660
661 The object is assumed to be either :term:`persistent` or
662 :term:`detached` when passed; after the method is called, the
663 object will remain in the :term:`persistent` state until the next
664 flush proceeds. During this time, the object will also be a member
665 of the :attr:`_orm.Session.deleted` collection.
666
667 When the next flush proceeds, the object will move to the
668 :term:`deleted` state, indicating a ``DELETE`` statement was emitted
669 for its row within the current transaction. When the transaction
670 is successfully committed,
671 the deleted object is moved to the :term:`detached` state and is
672 no longer present within this :class:`_orm.Session`.
673
674 .. seealso::
675
676 :ref:`session_deleting` - at :ref:`session_basics`
677
678 :meth:`.Session.delete_all` - multiple instance version
679
680
681 """ # noqa: E501
682
683 return self._proxied.delete(instance)
684
685 def delete_all(self, instances: Iterable[object]) -> None:
686 r"""Calls :meth:`.Session.delete` on multiple instances.
687
688 .. container:: class_bases
689
690 Proxied for the :class:`_orm.Session` class on
691 behalf of the :class:`_orm.scoping.scoped_session` class.
692
693 .. seealso::
694
695 :meth:`.Session.delete` - main documentation on delete
696
697 .. versionadded:: 2.1
698
699
700 """ # noqa: E501
701
702 return self._proxied.delete_all(instances)
703
704 @overload
705 def execute(
706 self,
707 statement: TypedReturnsRows[Unpack[_Ts]],
708 params: Optional[_CoreAnyExecuteParams] = None,
709 *,
710 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
711 bind_arguments: Optional[_BindArguments] = None,
712 _parent_execute_state: Optional[Any] = None,
713 _add_event: Optional[Any] = None,
714 ) -> Result[Unpack[_Ts]]: ...
715
716 @overload
717 def execute(
718 self,
719 statement: UpdateBase,
720 params: Optional[_CoreAnyExecuteParams] = None,
721 *,
722 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
723 bind_arguments: Optional[_BindArguments] = None,
724 _parent_execute_state: Optional[Any] = None,
725 _add_event: Optional[Any] = None,
726 ) -> CursorResult[Unpack[TupleAny]]: ...
727
728 @overload
729 def execute(
730 self,
731 statement: Executable,
732 params: Optional[_CoreAnyExecuteParams] = None,
733 *,
734 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
735 bind_arguments: Optional[_BindArguments] = None,
736 _parent_execute_state: Optional[Any] = None,
737 _add_event: Optional[Any] = None,
738 ) -> Result[Unpack[TupleAny]]: ...
739
740 def execute(
741 self,
742 statement: Executable,
743 params: Optional[_CoreAnyExecuteParams] = None,
744 *,
745 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
746 bind_arguments: Optional[_BindArguments] = None,
747 _parent_execute_state: Optional[Any] = None,
748 _add_event: Optional[Any] = None,
749 ) -> Result[Unpack[TupleAny]]:
750 r"""Execute a SQL expression construct.
751
752 .. container:: class_bases
753
754 Proxied for the :class:`_orm.Session` class on
755 behalf of the :class:`_orm.scoping.scoped_session` class.
756
757 Returns a :class:`_engine.Result` object representing
758 results of the statement execution.
759
760 E.g.::
761
762 from sqlalchemy import select
763
764 result = session.execute(select(User).where(User.id == 5))
765
766 The API contract of :meth:`_orm.Session.execute` is similar to that
767 of :meth:`_engine.Connection.execute`, the :term:`2.0 style` version
768 of :class:`_engine.Connection`.
769
770 .. versionchanged:: 1.4 the :meth:`_orm.Session.execute` method is
771 now the primary point of ORM statement execution when using
772 :term:`2.0 style` ORM usage.
773
774 :param statement:
775 An executable statement (i.e. an :class:`.Executable` expression
776 such as :func:`_expression.select`).
777
778 :param params:
779 Optional dictionary, or list of dictionaries, containing
780 bound parameter values. If a single dictionary, single-row
781 execution occurs; if a list of dictionaries, an
782 "executemany" will be invoked. The keys in each dictionary
783 must correspond to parameter names present in the statement.
784
785 :param execution_options: optional dictionary of execution options,
786 which will be associated with the statement execution. This
787 dictionary can provide a subset of the options that are accepted
788 by :meth:`_engine.Connection.execution_options`, and may also
789 provide additional options understood only in an ORM context.
790
791 .. seealso::
792
793 :ref:`orm_queryguide_execution_options` - ORM-specific execution
794 options
795
796 :param bind_arguments: dictionary of additional arguments to determine
797 the bind. May include "mapper", "bind", or other custom arguments.
798 Contents of this dictionary are passed to the
799 :meth:`.Session.get_bind` method.
800
801 :return: a :class:`_engine.Result` object.
802
803
804
805 """ # noqa: E501
806
807 return self._proxied.execute(
808 statement,
809 params=params,
810 execution_options=execution_options,
811 bind_arguments=bind_arguments,
812 _parent_execute_state=_parent_execute_state,
813 _add_event=_add_event,
814 )
815
816 def expire(
817 self, instance: object, attribute_names: Optional[Iterable[str]] = None
818 ) -> None:
819 r"""Expire the attributes on an instance.
820
821 .. container:: class_bases
822
823 Proxied for the :class:`_orm.Session` class on
824 behalf of the :class:`_orm.scoping.scoped_session` class.
825
826 Marks the attributes of an instance as out of date. When an expired
827 attribute is next accessed, a query will be issued to the
828 :class:`.Session` object's current transactional context in order to
829 load all expired attributes for the given instance. Note that
830 a highly isolated transaction will return the same values as were
831 previously read in that same transaction, regardless of changes
832 in database state outside of that transaction.
833
834 To expire all objects in the :class:`.Session` simultaneously,
835 use :meth:`Session.expire_all`.
836
837 The :class:`.Session` object's default behavior is to
838 expire all state whenever the :meth:`Session.rollback`
839 or :meth:`Session.commit` methods are called, so that new
840 state can be loaded for the new transaction. For this reason,
841 calling :meth:`Session.expire` only makes sense for the specific
842 case that a non-ORM SQL statement was emitted in the current
843 transaction.
844
845 :param instance: The instance to be refreshed.
846 :param attribute_names: optional list of string attribute names
847 indicating a subset of attributes to be expired.
848
849 .. seealso::
850
851 :ref:`session_expire` - introductory material
852
853 :meth:`.Session.expire`
854
855 :meth:`.Session.refresh`
856
857 :meth:`_orm.Query.populate_existing`
858
859
860 """ # noqa: E501
861
862 return self._proxied.expire(instance, attribute_names=attribute_names)
863
864 def expire_all(self) -> None:
865 r"""Expires all persistent instances within this Session.
866
867 .. container:: class_bases
868
869 Proxied for the :class:`_orm.Session` class on
870 behalf of the :class:`_orm.scoping.scoped_session` class.
871
872 When any attributes on a persistent instance is next accessed,
873 a query will be issued using the
874 :class:`.Session` object's current transactional context in order to
875 load all expired attributes for the given instance. Note that
876 a highly isolated transaction will return the same values as were
877 previously read in that same transaction, regardless of changes
878 in database state outside of that transaction.
879
880 To expire individual objects and individual attributes
881 on those objects, use :meth:`Session.expire`.
882
883 The :class:`.Session` object's default behavior is to
884 expire all state whenever the :meth:`Session.rollback`
885 or :meth:`Session.commit` methods are called, so that new
886 state can be loaded for the new transaction. For this reason,
887 calling :meth:`Session.expire_all` is not usually needed,
888 assuming the transaction is isolated.
889
890 .. seealso::
891
892 :ref:`session_expire` - introductory material
893
894 :meth:`.Session.expire`
895
896 :meth:`.Session.refresh`
897
898 :meth:`_orm.Query.populate_existing`
899
900
901 """ # noqa: E501
902
903 return self._proxied.expire_all()
904
905 def expunge(self, instance: object) -> None:
906 r"""Remove the `instance` from this ``Session``.
907
908 .. container:: class_bases
909
910 Proxied for the :class:`_orm.Session` class on
911 behalf of the :class:`_orm.scoping.scoped_session` class.
912
913 This will free all internal references to the instance. Cascading
914 will be applied according to the *expunge* cascade rule.
915
916
917 """ # noqa: E501
918
919 return self._proxied.expunge(instance)
920
921 def expunge_all(self) -> None:
922 r"""Remove all object instances from this ``Session``.
923
924 .. container:: class_bases
925
926 Proxied for the :class:`_orm.Session` class on
927 behalf of the :class:`_orm.scoping.scoped_session` class.
928
929 This is equivalent to calling ``expunge(obj)`` on all objects in this
930 ``Session``.
931
932
933 """ # noqa: E501
934
935 return self._proxied.expunge_all()
936
937 def flush(self, objects: Optional[Sequence[Any]] = None) -> None:
938 r"""Flush all the object changes to the database.
939
940 .. container:: class_bases
941
942 Proxied for the :class:`_orm.Session` class on
943 behalf of the :class:`_orm.scoping.scoped_session` class.
944
945 Writes out all pending object creations, deletions and modifications
946 to the database as INSERTs, DELETEs, UPDATEs, etc. Operations are
947 automatically ordered by the Session's unit of work dependency
948 solver.
949
950 Database operations will be issued in the current transactional
951 context and do not affect the state of the transaction, unless an
952 error occurs, in which case the entire transaction is rolled back.
953 You may flush() as often as you like within a transaction to move
954 changes from Python to the database's transaction buffer.
955
956 :param objects: Optional; restricts the flush operation to operate
957 only on elements that are in the given collection.
958
959 This feature is for an extremely narrow set of use cases where
960 particular objects may need to be operated upon before the
961 full flush() occurs. It is not intended for general use.
962
963 .. deprecated:: 2.1
964
965
966 """ # noqa: E501
967
968 return self._proxied.flush(objects=objects)
969
970 def get(
971 self,
972 entity: _EntityBindKey[_O],
973 ident: _PKIdentityArgument,
974 *,
975 options: Optional[Sequence[ORMOption]] = None,
976 populate_existing: bool = False,
977 with_for_update: ForUpdateParameter = None,
978 identity_token: Optional[Any] = None,
979 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
980 bind_arguments: Optional[_BindArguments] = None,
981 ) -> Optional[_O]:
982 r"""Return an instance based on the given primary key identifier,
983 or ``None`` if not found.
984
985 .. container:: class_bases
986
987 Proxied for the :class:`_orm.Session` class on
988 behalf of the :class:`_orm.scoping.scoped_session` class.
989
990 E.g.::
991
992 my_user = session.get(User, 5)
993
994 some_object = session.get(VersionedFoo, (5, 10))
995
996 some_object = session.get(VersionedFoo, {"id": 5, "version_id": 10})
997
998 .. versionadded:: 1.4 Added :meth:`_orm.Session.get`, which is moved
999 from the now legacy :meth:`_orm.Query.get` method.
1000
1001 :meth:`_orm.Session.get` is special in that it provides direct
1002 access to the identity map of the :class:`.Session`.
1003 If the given primary key identifier is present
1004 in the local identity map, the object is returned
1005 directly from this collection and no SQL is emitted,
1006 unless the object has been marked fully expired.
1007 If not present,
1008 a SELECT is performed in order to locate the object.
1009
1010 :meth:`_orm.Session.get` also will perform a check if
1011 the object is present in the identity map and
1012 marked as expired - a SELECT
1013 is emitted to refresh the object as well as to
1014 ensure that the row is still present.
1015 If not, :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
1016
1017 :param entity: a mapped class or :class:`.Mapper` indicating the
1018 type of entity to be loaded.
1019
1020 :param ident: A scalar, tuple, or dictionary representing the
1021 primary key. For a composite (e.g. multiple column) primary key,
1022 a tuple or dictionary should be passed.
1023
1024 For a single-column primary key, the scalar calling form is typically
1025 the most expedient. If the primary key of a row is the value "5",
1026 the call looks like::
1027
1028 my_object = session.get(SomeClass, 5)
1029
1030 The tuple form contains primary key values typically in
1031 the order in which they correspond to the mapped
1032 :class:`_schema.Table`
1033 object's primary key columns, or if the
1034 :paramref:`_orm.Mapper.primary_key` configuration parameter were
1035 used, in
1036 the order used for that parameter. For example, if the primary key
1037 of a row is represented by the integer
1038 digits "5, 10" the call would look like::
1039
1040 my_object = session.get(SomeClass, (5, 10))
1041
1042 The dictionary form should include as keys the mapped attribute names
1043 corresponding to each element of the primary key. If the mapped class
1044 has the attributes ``id``, ``version_id`` as the attributes which
1045 store the object's primary key value, the call would look like::
1046
1047 my_object = session.get(SomeClass, {"id": 5, "version_id": 10})
1048
1049 :param options: optional sequence of loader options which will be
1050 applied to the query, if one is emitted.
1051
1052 :param populate_existing: causes the method to unconditionally emit
1053 a SQL query and refresh the object with the newly loaded data,
1054 regardless of whether or not the object is already present.
1055
1056 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE
1057 should be used, or may be a dictionary containing flags to
1058 indicate a more specific set of FOR UPDATE flags for the SELECT;
1059 flags should match the parameters of
1060 :meth:`_query.Query.with_for_update`.
1061 Supersedes the :paramref:`.Session.refresh.lockmode` parameter.
1062
1063 :param execution_options: optional dictionary of execution options,
1064 which will be associated with the query execution if one is emitted.
1065 This dictionary can provide a subset of the options that are
1066 accepted by :meth:`_engine.Connection.execution_options`, and may
1067 also provide additional options understood only in an ORM context.
1068
1069 .. versionadded:: 1.4.29
1070
1071 .. seealso::
1072
1073 :ref:`orm_queryguide_execution_options` - ORM-specific execution
1074 options
1075
1076 :param bind_arguments: dictionary of additional arguments to determine
1077 the bind. May include "mapper", "bind", or other custom arguments.
1078 Contents of this dictionary are passed to the
1079 :meth:`.Session.get_bind` method.
1080
1081 .. versionadded:: 2.0.0rc1
1082
1083 :return: The object instance, or ``None``.
1084
1085
1086 """ # noqa: E501
1087
1088 return self._proxied.get(
1089 entity,
1090 ident,
1091 options=options,
1092 populate_existing=populate_existing,
1093 with_for_update=with_for_update,
1094 identity_token=identity_token,
1095 execution_options=execution_options,
1096 bind_arguments=bind_arguments,
1097 )
1098
1099 def get_one(
1100 self,
1101 entity: _EntityBindKey[_O],
1102 ident: _PKIdentityArgument,
1103 *,
1104 options: Optional[Sequence[ORMOption]] = None,
1105 populate_existing: bool = False,
1106 with_for_update: ForUpdateParameter = None,
1107 identity_token: Optional[Any] = None,
1108 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1109 bind_arguments: Optional[_BindArguments] = None,
1110 ) -> _O:
1111 r"""Return exactly one instance based on the given primary key
1112 identifier, or raise an exception if not found.
1113
1114 .. container:: class_bases
1115
1116 Proxied for the :class:`_orm.Session` class on
1117 behalf of the :class:`_orm.scoping.scoped_session` class.
1118
1119 Raises :class:`_exc.NoResultFound` if the query selects no rows.
1120
1121 For a detailed documentation of the arguments see the
1122 method :meth:`.Session.get`.
1123
1124 .. versionadded:: 2.0.22
1125
1126 :return: The object instance.
1127
1128 .. seealso::
1129
1130 :meth:`.Session.get` - equivalent method that instead
1131 returns ``None`` if no row was found with the provided primary
1132 key
1133
1134
1135 """ # noqa: E501
1136
1137 return self._proxied.get_one(
1138 entity,
1139 ident,
1140 options=options,
1141 populate_existing=populate_existing,
1142 with_for_update=with_for_update,
1143 identity_token=identity_token,
1144 execution_options=execution_options,
1145 bind_arguments=bind_arguments,
1146 )
1147
1148 def get_bind(
1149 self,
1150 mapper: Optional[_EntityBindKey[_O]] = None,
1151 *,
1152 clause: Optional[ClauseElement] = None,
1153 bind: Optional[_SessionBind] = None,
1154 _sa_skip_events: Optional[bool] = None,
1155 _sa_skip_for_implicit_returning: bool = False,
1156 **kw: Any,
1157 ) -> Union[Engine, Connection]:
1158 r"""Return a "bind" to which this :class:`.Session` is bound.
1159
1160 .. container:: class_bases
1161
1162 Proxied for the :class:`_orm.Session` class on
1163 behalf of the :class:`_orm.scoping.scoped_session` class.
1164
1165 The "bind" is usually an instance of :class:`_engine.Engine`,
1166 except in the case where the :class:`.Session` has been
1167 explicitly bound directly to a :class:`_engine.Connection`.
1168
1169 For a multiply-bound or unbound :class:`.Session`, the
1170 ``mapper`` or ``clause`` arguments are used to determine the
1171 appropriate bind to return.
1172
1173 Note that the "mapper" argument is usually present
1174 when :meth:`.Session.get_bind` is called via an ORM
1175 operation such as a :meth:`.Session.query`, each
1176 individual INSERT/UPDATE/DELETE operation within a
1177 :meth:`.Session.flush`, call, etc.
1178
1179 The order of resolution is:
1180
1181 1. if mapper given and :paramref:`.Session.binds` is present,
1182 locate a bind based first on the mapper in use, then
1183 on the mapped class in use, then on any base classes that are
1184 present in the ``__mro__`` of the mapped class, from more specific
1185 superclasses to more general.
1186 2. if clause given and ``Session.binds`` is present,
1187 locate a bind based on :class:`_schema.Table` objects
1188 found in the given clause present in ``Session.binds``.
1189 3. if ``Session.binds`` is present, return that.
1190 4. if clause given, attempt to return a bind
1191 linked to the :class:`_schema.MetaData` ultimately
1192 associated with the clause.
1193 5. if mapper given, attempt to return a bind
1194 linked to the :class:`_schema.MetaData` ultimately
1195 associated with the :class:`_schema.Table` or other
1196 selectable to which the mapper is mapped.
1197 6. No bind can be found, :exc:`~sqlalchemy.exc.UnboundExecutionError`
1198 is raised.
1199
1200 Note that the :meth:`.Session.get_bind` method can be overridden on
1201 a user-defined subclass of :class:`.Session` to provide any kind
1202 of bind resolution scheme. See the example at
1203 :ref:`session_custom_partitioning`.
1204
1205 :param mapper:
1206 Optional mapped class or corresponding :class:`_orm.Mapper` instance.
1207 The bind can be derived from a :class:`_orm.Mapper` first by
1208 consulting the "binds" map associated with this :class:`.Session`,
1209 and secondly by consulting the :class:`_schema.MetaData` associated
1210 with the :class:`_schema.Table` to which the :class:`_orm.Mapper` is
1211 mapped for a bind.
1212
1213 :param clause:
1214 A :class:`_expression.ClauseElement` (i.e.
1215 :func:`_expression.select`,
1216 :func:`_expression.text`,
1217 etc.). If the ``mapper`` argument is not present or could not
1218 produce a bind, the given expression construct will be searched
1219 for a bound element, typically a :class:`_schema.Table`
1220 associated with
1221 bound :class:`_schema.MetaData`.
1222
1223 .. seealso::
1224
1225 :ref:`session_partitioning`
1226
1227 :paramref:`.Session.binds`
1228
1229 :meth:`.Session.bind_mapper`
1230
1231 :meth:`.Session.bind_table`
1232
1233
1234 """ # noqa: E501
1235
1236 return self._proxied.get_bind(
1237 mapper=mapper,
1238 clause=clause,
1239 bind=bind,
1240 _sa_skip_events=_sa_skip_events,
1241 _sa_skip_for_implicit_returning=_sa_skip_for_implicit_returning,
1242 **kw,
1243 )
1244
1245 def is_modified(
1246 self, instance: object, include_collections: bool = True
1247 ) -> bool:
1248 r"""Return ``True`` if the given instance has locally
1249 modified attributes.
1250
1251 .. container:: class_bases
1252
1253 Proxied for the :class:`_orm.Session` class on
1254 behalf of the :class:`_orm.scoping.scoped_session` class.
1255
1256 This method retrieves the history for each instrumented
1257 attribute on the instance and performs a comparison of the current
1258 value to its previously flushed or committed value, if any.
1259
1260 It is in effect a more expensive and accurate
1261 version of checking for the given instance in the
1262 :attr:`.Session.dirty` collection; a full test for
1263 each attribute's net "dirty" status is performed.
1264
1265 E.g.::
1266
1267 return session.is_modified(someobject)
1268
1269 A few caveats to this method apply:
1270
1271 * Instances present in the :attr:`.Session.dirty` collection may
1272 report ``False`` when tested with this method. This is because
1273 the object may have received change events via attribute mutation,
1274 thus placing it in :attr:`.Session.dirty`, but ultimately the state
1275 is the same as that loaded from the database, resulting in no net
1276 change here.
1277 * Scalar attributes may not have recorded the previously set
1278 value when a new value was applied, if the attribute was not loaded,
1279 or was expired, at the time the new value was received - in these
1280 cases, the attribute is assumed to have a change, even if there is
1281 ultimately no net change against its database value. SQLAlchemy in
1282 most cases does not need the "old" value when a set event occurs, so
1283 it skips the expense of a SQL call if the old value isn't present,
1284 based on the assumption that an UPDATE of the scalar value is
1285 usually needed, and in those few cases where it isn't, is less
1286 expensive on average than issuing a defensive SELECT.
1287
1288 The "old" value is fetched unconditionally upon set only if the
1289 attribute container has the ``active_history`` flag set to ``True``.
1290 This flag is set typically for primary key attributes and scalar
1291 object references that are not a simple many-to-one. To set this
1292 flag for any arbitrary mapped column, use the ``active_history``
1293 argument with :func:`.column_property`.
1294
1295 :param instance: mapped instance to be tested for pending changes.
1296 :param include_collections: Indicates if multivalued collections
1297 should be included in the operation. Setting this to ``False`` is a
1298 way to detect only local-column based properties (i.e. scalar columns
1299 or many-to-one foreign keys) that would result in an UPDATE for this
1300 instance upon flush.
1301
1302
1303 """ # noqa: E501
1304
1305 return self._proxied.is_modified(
1306 instance, include_collections=include_collections
1307 )
1308
1309 def bulk_save_objects(
1310 self,
1311 objects: Iterable[object],
1312 return_defaults: bool = False,
1313 update_changed_only: bool = True,
1314 preserve_order: bool = True,
1315 ) -> None:
1316 r"""Perform a bulk save of the given list of objects.
1317
1318 .. container:: class_bases
1319
1320 Proxied for the :class:`_orm.Session` class on
1321 behalf of the :class:`_orm.scoping.scoped_session` class.
1322
1323 .. legacy::
1324
1325 This method is a legacy feature as of the 2.0 series of
1326 SQLAlchemy. For modern bulk INSERT and UPDATE, see
1327 the sections :ref:`orm_queryguide_bulk_insert` and
1328 :ref:`orm_queryguide_bulk_update`.
1329
1330 For general INSERT and UPDATE of existing ORM mapped objects,
1331 prefer standard :term:`unit of work` data management patterns,
1332 introduced in the :ref:`unified_tutorial` at
1333 :ref:`tutorial_orm_data_manipulation`. SQLAlchemy 2.0
1334 now uses :ref:`engine_insertmanyvalues` with modern dialects
1335 which solves previous issues of bulk INSERT slowness.
1336
1337 :param objects: a sequence of mapped object instances. The mapped
1338 objects are persisted as is, and are **not** associated with the
1339 :class:`.Session` afterwards.
1340
1341 For each object, whether the object is sent as an INSERT or an
1342 UPDATE is dependent on the same rules used by the :class:`.Session`
1343 in traditional operation; if the object has the
1344 :attr:`.InstanceState.key`
1345 attribute set, then the object is assumed to be "detached" and
1346 will result in an UPDATE. Otherwise, an INSERT is used.
1347
1348 In the case of an UPDATE, statements are grouped based on which
1349 attributes have changed, and are thus to be the subject of each
1350 SET clause. If ``update_changed_only`` is False, then all
1351 attributes present within each object are applied to the UPDATE
1352 statement, which may help in allowing the statements to be grouped
1353 together into a larger executemany(), and will also reduce the
1354 overhead of checking history on attributes.
1355
1356 :param return_defaults: when True, rows that are missing values which
1357 generate defaults, namely integer primary key defaults and sequences,
1358 will be inserted **one at a time**, so that the primary key value
1359 is available. In particular this will allow joined-inheritance
1360 and other multi-table mappings to insert correctly without the need
1361 to provide primary key values ahead of time; however,
1362 :paramref:`.Session.bulk_save_objects.return_defaults` **greatly
1363 reduces the performance gains** of the method overall. It is strongly
1364 advised to please use the standard :meth:`_orm.Session.add_all`
1365 approach.
1366
1367 :param update_changed_only: when True, UPDATE statements are rendered
1368 based on those attributes in each state that have logged changes.
1369 When False, all attributes present are rendered into the SET clause
1370 with the exception of primary key attributes.
1371
1372 :param preserve_order: when True, the order of inserts and updates
1373 matches exactly the order in which the objects are given. When
1374 False, common types of objects are grouped into inserts
1375 and updates, to allow for more batching opportunities.
1376
1377 .. seealso::
1378
1379 :doc:`queryguide/dml`
1380
1381 :meth:`.Session.bulk_insert_mappings`
1382
1383 :meth:`.Session.bulk_update_mappings`
1384
1385
1386 """ # noqa: E501
1387
1388 return self._proxied.bulk_save_objects(
1389 objects,
1390 return_defaults=return_defaults,
1391 update_changed_only=update_changed_only,
1392 preserve_order=preserve_order,
1393 )
1394
1395 def bulk_insert_mappings(
1396 self,
1397 mapper: Mapper[Any],
1398 mappings: Iterable[Dict[str, Any]],
1399 return_defaults: bool = False,
1400 render_nulls: bool = False,
1401 ) -> None:
1402 r"""Perform a bulk insert of the given list of mapping dictionaries.
1403
1404 .. container:: class_bases
1405
1406 Proxied for the :class:`_orm.Session` class on
1407 behalf of the :class:`_orm.scoping.scoped_session` class.
1408
1409 .. legacy::
1410
1411 This method is a legacy feature as of the 2.0 series of
1412 SQLAlchemy. For modern bulk INSERT and UPDATE, see
1413 the sections :ref:`orm_queryguide_bulk_insert` and
1414 :ref:`orm_queryguide_bulk_update`. The 2.0 API shares
1415 implementation details with this method and adds new features
1416 as well.
1417
1418 :param mapper: a mapped class, or the actual :class:`_orm.Mapper`
1419 object,
1420 representing the single kind of object represented within the mapping
1421 list.
1422
1423 :param mappings: a sequence of dictionaries, each one containing the
1424 state of the mapped row to be inserted, in terms of the attribute
1425 names on the mapped class. If the mapping refers to multiple tables,
1426 such as a joined-inheritance mapping, each dictionary must contain all
1427 keys to be populated into all tables.
1428
1429 :param return_defaults: when True, the INSERT process will be altered
1430 to ensure that newly generated primary key values will be fetched.
1431 The rationale for this parameter is typically to enable
1432 :ref:`Joined Table Inheritance <joined_inheritance>` mappings to
1433 be bulk inserted.
1434
1435 .. note:: for backends that don't support RETURNING, the
1436 :paramref:`_orm.Session.bulk_insert_mappings.return_defaults`
1437 parameter can significantly decrease performance as INSERT
1438 statements can no longer be batched. See
1439 :ref:`engine_insertmanyvalues`
1440 for background on which backends are affected.
1441
1442 :param render_nulls: When True, a value of ``None`` will result
1443 in a NULL value being included in the INSERT statement, rather
1444 than the column being omitted from the INSERT. This allows all
1445 the rows being INSERTed to have the identical set of columns which
1446 allows the full set of rows to be batched to the DBAPI. Normally,
1447 each column-set that contains a different combination of NULL values
1448 than the previous row must omit a different series of columns from
1449 the rendered INSERT statement, which means it must be emitted as a
1450 separate statement. By passing this flag, the full set of rows
1451 are guaranteed to be batchable into one batch; the cost however is
1452 that server-side defaults which are invoked by an omitted column will
1453 be skipped, so care must be taken to ensure that these are not
1454 necessary.
1455
1456 .. warning::
1457
1458 When this flag is set, **server side default SQL values will
1459 not be invoked** for those columns that are inserted as NULL;
1460 the NULL value will be sent explicitly. Care must be taken
1461 to ensure that no server-side default functions need to be
1462 invoked for the operation as a whole.
1463
1464 .. seealso::
1465
1466 :doc:`queryguide/dml`
1467
1468 :meth:`.Session.bulk_save_objects`
1469
1470 :meth:`.Session.bulk_update_mappings`
1471
1472
1473 """ # noqa: E501
1474
1475 return self._proxied.bulk_insert_mappings(
1476 mapper,
1477 mappings,
1478 return_defaults=return_defaults,
1479 render_nulls=render_nulls,
1480 )
1481
1482 def bulk_update_mappings(
1483 self, mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]]
1484 ) -> None:
1485 r"""Perform a bulk update of the given list of mapping dictionaries.
1486
1487 .. container:: class_bases
1488
1489 Proxied for the :class:`_orm.Session` class on
1490 behalf of the :class:`_orm.scoping.scoped_session` class.
1491
1492 .. legacy::
1493
1494 This method is a legacy feature as of the 2.0 series of
1495 SQLAlchemy. For modern bulk INSERT and UPDATE, see
1496 the sections :ref:`orm_queryguide_bulk_insert` and
1497 :ref:`orm_queryguide_bulk_update`. The 2.0 API shares
1498 implementation details with this method and adds new features
1499 as well.
1500
1501 :param mapper: a mapped class, or the actual :class:`_orm.Mapper`
1502 object,
1503 representing the single kind of object represented within the mapping
1504 list.
1505
1506 :param mappings: a sequence of dictionaries, each one containing the
1507 state of the mapped row to be updated, in terms of the attribute names
1508 on the mapped class. If the mapping refers to multiple tables, such
1509 as a joined-inheritance mapping, each dictionary may contain keys
1510 corresponding to all tables. All those keys which are present and
1511 are not part of the primary key are applied to the SET clause of the
1512 UPDATE statement; the primary key values, which are required, are
1513 applied to the WHERE clause.
1514
1515
1516 .. seealso::
1517
1518 :doc:`queryguide/dml`
1519
1520 :meth:`.Session.bulk_insert_mappings`
1521
1522 :meth:`.Session.bulk_save_objects`
1523
1524
1525 """ # noqa: E501
1526
1527 return self._proxied.bulk_update_mappings(mapper, mappings)
1528
1529 def merge(
1530 self,
1531 instance: _O,
1532 *,
1533 load: bool = True,
1534 options: Optional[Sequence[ORMOption]] = None,
1535 ) -> _O:
1536 r"""Copy the state of a given instance into a corresponding instance
1537 within this :class:`.Session`.
1538
1539 .. container:: class_bases
1540
1541 Proxied for the :class:`_orm.Session` class on
1542 behalf of the :class:`_orm.scoping.scoped_session` class.
1543
1544 :meth:`.Session.merge` examines the primary key attributes of the
1545 source instance, and attempts to reconcile it with an instance of the
1546 same primary key in the session. If not found locally, it attempts
1547 to load the object from the database based on primary key, and if
1548 none can be located, creates a new instance. The state of each
1549 attribute on the source instance is then copied to the target
1550 instance. The resulting target instance is then returned by the
1551 method; the original source instance is left unmodified, and
1552 un-associated with the :class:`.Session` if not already.
1553
1554 This operation cascades to associated instances if the association is
1555 mapped with ``cascade="merge"``.
1556
1557 See :ref:`unitofwork_merging` for a detailed discussion of merging.
1558
1559 :param instance: Instance to be merged.
1560 :param load: Boolean, when False, :meth:`.merge` switches into
1561 a "high performance" mode which causes it to forego emitting history
1562 events as well as all database access. This flag is used for
1563 cases such as transferring graphs of objects into a :class:`.Session`
1564 from a second level cache, or to transfer just-loaded objects
1565 into the :class:`.Session` owned by a worker thread or process
1566 without re-querying the database.
1567
1568 The ``load=False`` use case adds the caveat that the given
1569 object has to be in a "clean" state, that is, has no pending changes
1570 to be flushed - even if the incoming object is detached from any
1571 :class:`.Session`. This is so that when
1572 the merge operation populates local attributes and
1573 cascades to related objects and
1574 collections, the values can be "stamped" onto the
1575 target object as is, without generating any history or attribute
1576 events, and without the need to reconcile the incoming data with
1577 any existing related objects or collections that might not
1578 be loaded. The resulting objects from ``load=False`` are always
1579 produced as "clean", so it is only appropriate that the given objects
1580 should be "clean" as well, else this suggests a mis-use of the
1581 method.
1582 :param options: optional sequence of loader options which will be
1583 applied to the :meth:`_orm.Session.get` method when the merge
1584 operation loads the existing version of the object from the database.
1585
1586 .. versionadded:: 1.4.24
1587
1588
1589 .. seealso::
1590
1591 :func:`.make_transient_to_detached` - provides for an alternative
1592 means of "merging" a single object into the :class:`.Session`
1593
1594 :meth:`.Session.merge_all` - multiple instance version
1595
1596
1597 """ # noqa: E501
1598
1599 return self._proxied.merge(instance, load=load, options=options)
1600
1601 def merge_all(
1602 self,
1603 instances: Iterable[_O],
1604 *,
1605 load: bool = True,
1606 options: Optional[Sequence[ORMOption]] = None,
1607 ) -> Sequence[_O]:
1608 r"""Calls :meth:`.Session.merge` on multiple instances.
1609
1610 .. container:: class_bases
1611
1612 Proxied for the :class:`_orm.Session` class on
1613 behalf of the :class:`_orm.scoping.scoped_session` class.
1614
1615 .. seealso::
1616
1617 :meth:`.Session.merge` - main documentation on merge
1618
1619 .. versionadded:: 2.1
1620
1621
1622 """ # noqa: E501
1623
1624 return self._proxied.merge_all(instances, load=load, options=options)
1625
1626 @overload
1627 def query(self, _entity: _EntityType[_O]) -> Query[_O]: ...
1628
1629 @overload
1630 def query(
1631 self, _colexpr: TypedColumnsClauseRole[_T]
1632 ) -> RowReturningQuery[_T]: ...
1633
1634 # START OVERLOADED FUNCTIONS self.query RowReturningQuery 2-8
1635
1636 # code within this block is **programmatically,
1637 # statically generated** by tools/generate_tuple_map_overloads.py
1638
1639 @overload
1640 def query(
1641 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], /
1642 ) -> RowReturningQuery[_T0, _T1]: ...
1643
1644 @overload
1645 def query(
1646 self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2], /
1647 ) -> RowReturningQuery[_T0, _T1, _T2]: ...
1648
1649 @overload
1650 def query(
1651 self,
1652 __ent0: _TCCA[_T0],
1653 __ent1: _TCCA[_T1],
1654 __ent2: _TCCA[_T2],
1655 __ent3: _TCCA[_T3],
1656 /,
1657 ) -> RowReturningQuery[_T0, _T1, _T2, _T3]: ...
1658
1659 @overload
1660 def query(
1661 self,
1662 __ent0: _TCCA[_T0],
1663 __ent1: _TCCA[_T1],
1664 __ent2: _TCCA[_T2],
1665 __ent3: _TCCA[_T3],
1666 __ent4: _TCCA[_T4],
1667 /,
1668 ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4]: ...
1669
1670 @overload
1671 def query(
1672 self,
1673 __ent0: _TCCA[_T0],
1674 __ent1: _TCCA[_T1],
1675 __ent2: _TCCA[_T2],
1676 __ent3: _TCCA[_T3],
1677 __ent4: _TCCA[_T4],
1678 __ent5: _TCCA[_T5],
1679 /,
1680 ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4, _T5]: ...
1681
1682 @overload
1683 def query(
1684 self,
1685 __ent0: _TCCA[_T0],
1686 __ent1: _TCCA[_T1],
1687 __ent2: _TCCA[_T2],
1688 __ent3: _TCCA[_T3],
1689 __ent4: _TCCA[_T4],
1690 __ent5: _TCCA[_T5],
1691 __ent6: _TCCA[_T6],
1692 /,
1693 ) -> RowReturningQuery[_T0, _T1, _T2, _T3, _T4, _T5, _T6]: ...
1694
1695 @overload
1696 def query(
1697 self,
1698 __ent0: _TCCA[_T0],
1699 __ent1: _TCCA[_T1],
1700 __ent2: _TCCA[_T2],
1701 __ent3: _TCCA[_T3],
1702 __ent4: _TCCA[_T4],
1703 __ent5: _TCCA[_T5],
1704 __ent6: _TCCA[_T6],
1705 __ent7: _TCCA[_T7],
1706 /,
1707 *entities: _ColumnsClauseArgument[Any],
1708 ) -> RowReturningQuery[
1709 _T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, Unpack[TupleAny]
1710 ]: ...
1711
1712 # END OVERLOADED FUNCTIONS self.query
1713
1714 @overload
1715 def query(
1716 self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
1717 ) -> Query[Any]: ...
1718
1719 def query(
1720 self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
1721 ) -> Query[Any]:
1722 r"""Return a new :class:`_query.Query` object corresponding to this
1723 :class:`_orm.Session`.
1724
1725 .. container:: class_bases
1726
1727 Proxied for the :class:`_orm.Session` class on
1728 behalf of the :class:`_orm.scoping.scoped_session` class.
1729
1730 Note that the :class:`_query.Query` object is legacy as of
1731 SQLAlchemy 2.0; the :func:`_sql.select` construct is now used
1732 to construct ORM queries.
1733
1734 .. seealso::
1735
1736 :ref:`unified_tutorial`
1737
1738 :ref:`queryguide_toplevel`
1739
1740 :ref:`query_api_toplevel` - legacy API doc
1741
1742
1743 """ # noqa: E501
1744
1745 return self._proxied.query(*entities, **kwargs)
1746
1747 def refresh(
1748 self,
1749 instance: object,
1750 attribute_names: Optional[Iterable[str]] = None,
1751 with_for_update: ForUpdateParameter = None,
1752 ) -> None:
1753 r"""Expire and refresh attributes on the given instance.
1754
1755 .. container:: class_bases
1756
1757 Proxied for the :class:`_orm.Session` class on
1758 behalf of the :class:`_orm.scoping.scoped_session` class.
1759
1760 The selected attributes will first be expired as they would when using
1761 :meth:`_orm.Session.expire`; then a SELECT statement will be issued to
1762 the database to refresh column-oriented attributes with the current
1763 value available in the current transaction.
1764
1765 :func:`_orm.relationship` oriented attributes will also be immediately
1766 loaded if they were already eagerly loaded on the object, using the
1767 same eager loading strategy that they were loaded with originally.
1768
1769 .. versionadded:: 1.4 - the :meth:`_orm.Session.refresh` method
1770 can also refresh eagerly loaded attributes.
1771
1772 :func:`_orm.relationship` oriented attributes that would normally
1773 load using the ``select`` (or "lazy") loader strategy will also
1774 load **if they are named explicitly in the attribute_names
1775 collection**, emitting a SELECT statement for the attribute using the
1776 ``immediate`` loader strategy. If lazy-loaded relationships are not
1777 named in :paramref:`_orm.Session.refresh.attribute_names`, then
1778 they remain as "lazy loaded" attributes and are not implicitly
1779 refreshed.
1780
1781 .. versionchanged:: 2.0.4 The :meth:`_orm.Session.refresh` method
1782 will now refresh lazy-loaded :func:`_orm.relationship` oriented
1783 attributes for those which are named explicitly in the
1784 :paramref:`_orm.Session.refresh.attribute_names` collection.
1785
1786 .. tip::
1787
1788 While the :meth:`_orm.Session.refresh` method is capable of
1789 refreshing both column and relationship oriented attributes, its
1790 primary focus is on refreshing of local column-oriented attributes
1791 on a single instance. For more open ended "refresh" functionality,
1792 including the ability to refresh the attributes on many objects at
1793 once while having explicit control over relationship loader
1794 strategies, use the
1795 :ref:`populate existing <orm_queryguide_populate_existing>` feature
1796 instead.
1797
1798 Note that a highly isolated transaction will return the same values as
1799 were previously read in that same transaction, regardless of changes
1800 in database state outside of that transaction. Refreshing
1801 attributes usually only makes sense at the start of a transaction
1802 where database rows have not yet been accessed.
1803
1804 :param attribute_names: optional. An iterable collection of
1805 string attribute names indicating a subset of attributes to
1806 be refreshed.
1807
1808 :param with_for_update: optional boolean ``True`` indicating FOR UPDATE
1809 should be used, or may be a dictionary containing flags to
1810 indicate a more specific set of FOR UPDATE flags for the SELECT;
1811 flags should match the parameters of
1812 :meth:`_query.Query.with_for_update`.
1813 Supersedes the :paramref:`.Session.refresh.lockmode` parameter.
1814
1815 .. seealso::
1816
1817 :ref:`session_expire` - introductory material
1818
1819 :meth:`.Session.expire`
1820
1821 :meth:`.Session.expire_all`
1822
1823 :ref:`orm_queryguide_populate_existing` - allows any ORM query
1824 to refresh objects as they would be loaded normally.
1825
1826
1827 """ # noqa: E501
1828
1829 return self._proxied.refresh(
1830 instance,
1831 attribute_names=attribute_names,
1832 with_for_update=with_for_update,
1833 )
1834
1835 def rollback(self) -> None:
1836 r"""Rollback the current transaction in progress.
1837
1838 .. container:: class_bases
1839
1840 Proxied for the :class:`_orm.Session` class on
1841 behalf of the :class:`_orm.scoping.scoped_session` class.
1842
1843 If no transaction is in progress, this method is a pass-through.
1844
1845 The method always rolls back
1846 the topmost database transaction, discarding any nested
1847 transactions that may be in progress.
1848
1849 .. seealso::
1850
1851 :ref:`session_rollback`
1852
1853 :ref:`unitofwork_transaction`
1854
1855
1856 """ # noqa: E501
1857
1858 return self._proxied.rollback()
1859
1860 @overload
1861 def scalar(
1862 self,
1863 statement: TypedReturnsRows[_T],
1864 params: Optional[_CoreSingleExecuteParams] = None,
1865 *,
1866 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1867 bind_arguments: Optional[_BindArguments] = None,
1868 **kw: Any,
1869 ) -> Optional[_T]: ...
1870
1871 @overload
1872 def scalar(
1873 self,
1874 statement: Executable,
1875 params: Optional[_CoreSingleExecuteParams] = None,
1876 *,
1877 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1878 bind_arguments: Optional[_BindArguments] = None,
1879 **kw: Any,
1880 ) -> Any: ...
1881
1882 def scalar(
1883 self,
1884 statement: Executable,
1885 params: Optional[_CoreSingleExecuteParams] = None,
1886 *,
1887 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1888 bind_arguments: Optional[_BindArguments] = None,
1889 **kw: Any,
1890 ) -> Any:
1891 r"""Execute a statement and return a scalar result.
1892
1893 .. container:: class_bases
1894
1895 Proxied for the :class:`_orm.Session` class on
1896 behalf of the :class:`_orm.scoping.scoped_session` class.
1897
1898 Usage and parameters are the same as that of
1899 :meth:`_orm.Session.execute`; the return result is a scalar Python
1900 value.
1901
1902
1903 """ # noqa: E501
1904
1905 return self._proxied.scalar(
1906 statement,
1907 params=params,
1908 execution_options=execution_options,
1909 bind_arguments=bind_arguments,
1910 **kw,
1911 )
1912
1913 @overload
1914 def scalars(
1915 self,
1916 statement: TypedReturnsRows[_T],
1917 params: Optional[_CoreAnyExecuteParams] = None,
1918 *,
1919 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1920 bind_arguments: Optional[_BindArguments] = None,
1921 **kw: Any,
1922 ) -> ScalarResult[_T]: ...
1923
1924 @overload
1925 def scalars(
1926 self,
1927 statement: Executable,
1928 params: Optional[_CoreAnyExecuteParams] = None,
1929 *,
1930 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1931 bind_arguments: Optional[_BindArguments] = None,
1932 **kw: Any,
1933 ) -> ScalarResult[Any]: ...
1934
1935 def scalars(
1936 self,
1937 statement: Executable,
1938 params: Optional[_CoreAnyExecuteParams] = None,
1939 *,
1940 execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
1941 bind_arguments: Optional[_BindArguments] = None,
1942 **kw: Any,
1943 ) -> ScalarResult[Any]:
1944 r"""Execute a statement and return the results as scalars.
1945
1946 .. container:: class_bases
1947
1948 Proxied for the :class:`_orm.Session` class on
1949 behalf of the :class:`_orm.scoping.scoped_session` class.
1950
1951 Usage and parameters are the same as that of
1952 :meth:`_orm.Session.execute`; the return result is a
1953 :class:`_result.ScalarResult` filtering object which
1954 will return single elements rather than :class:`_row.Row` objects.
1955
1956 :return: a :class:`_result.ScalarResult` object
1957
1958 .. versionadded:: 1.4.24 Added :meth:`_orm.Session.scalars`
1959
1960 .. versionadded:: 1.4.26 Added :meth:`_orm.scoped_session.scalars`
1961
1962 .. seealso::
1963
1964 :ref:`orm_queryguide_select_orm_entities` - contrasts the behavior
1965 of :meth:`_orm.Session.execute` to :meth:`_orm.Session.scalars`
1966
1967
1968 """ # noqa: E501
1969
1970 return self._proxied.scalars(
1971 statement,
1972 params=params,
1973 execution_options=execution_options,
1974 bind_arguments=bind_arguments,
1975 **kw,
1976 )
1977
1978 @property
1979 def bind(self) -> Optional[Union[Engine, Connection]]:
1980 r"""Proxy for the :attr:`_orm.Session.bind` attribute
1981 on behalf of the :class:`_orm.scoping.scoped_session` class.
1982
1983 """ # noqa: E501
1984
1985 return self._proxied.bind
1986
1987 @bind.setter
1988 def bind(self, attr: Optional[Union[Engine, Connection]]) -> None:
1989 self._proxied.bind = attr
1990
1991 @property
1992 def dirty(self) -> Any:
1993 r"""The set of all persistent instances considered dirty.
1994
1995 .. container:: class_bases
1996
1997 Proxied for the :class:`_orm.Session` class
1998 on behalf of the :class:`_orm.scoping.scoped_session` class.
1999
2000 E.g.::
2001
2002 some_mapped_object in session.dirty
2003
2004 Instances are considered dirty when they were modified but not
2005 deleted.
2006
2007 Note that this 'dirty' calculation is 'optimistic'; most
2008 attribute-setting or collection modification operations will
2009 mark an instance as 'dirty' and place it in this set, even if
2010 there is no net change to the attribute's value. At flush
2011 time, the value of each attribute is compared to its
2012 previously saved value, and if there's no net change, no SQL
2013 operation will occur (this is a more expensive operation so
2014 it's only done at flush time).
2015
2016 To check if an instance has actionable net changes to its
2017 attributes, use the :meth:`.Session.is_modified` method.
2018
2019
2020 """ # noqa: E501
2021
2022 return self._proxied.dirty
2023
2024 @property
2025 def deleted(self) -> Any:
2026 r"""The set of all instances marked as 'deleted' within this ``Session``
2027
2028 .. container:: class_bases
2029
2030 Proxied for the :class:`_orm.Session` class
2031 on behalf of the :class:`_orm.scoping.scoped_session` class.
2032
2033 """ # noqa: E501
2034
2035 return self._proxied.deleted
2036
2037 @property
2038 def new(self) -> Any:
2039 r"""The set of all instances marked as 'new' within this ``Session``.
2040
2041 .. container:: class_bases
2042
2043 Proxied for the :class:`_orm.Session` class
2044 on behalf of the :class:`_orm.scoping.scoped_session` class.
2045
2046 """ # noqa: E501
2047
2048 return self._proxied.new
2049
2050 @property
2051 def identity_map(self) -> IdentityMap:
2052 r"""Proxy for the :attr:`_orm.Session.identity_map` attribute
2053 on behalf of the :class:`_orm.scoping.scoped_session` class.
2054
2055 """ # noqa: E501
2056
2057 return self._proxied.identity_map
2058
2059 @identity_map.setter
2060 def identity_map(self, attr: IdentityMap) -> None:
2061 self._proxied.identity_map = attr
2062
2063 @property
2064 def is_active(self) -> Any:
2065 r"""True if this :class:`.Session` not in "partial rollback" state.
2066
2067 .. container:: class_bases
2068
2069 Proxied for the :class:`_orm.Session` class
2070 on behalf of the :class:`_orm.scoping.scoped_session` class.
2071
2072 .. versionchanged:: 1.4 The :class:`_orm.Session` no longer begins
2073 a new transaction immediately, so this attribute will be False
2074 when the :class:`_orm.Session` is first instantiated.
2075
2076 "partial rollback" state typically indicates that the flush process
2077 of the :class:`_orm.Session` has failed, and that the
2078 :meth:`_orm.Session.rollback` method must be emitted in order to
2079 fully roll back the transaction.
2080
2081 If this :class:`_orm.Session` is not in a transaction at all, the
2082 :class:`_orm.Session` will autobegin when it is first used, so in this
2083 case :attr:`_orm.Session.is_active` will return True.
2084
2085 Otherwise, if this :class:`_orm.Session` is within a transaction,
2086 and that transaction has not been rolled back internally, the
2087 :attr:`_orm.Session.is_active` will also return True.
2088
2089 .. seealso::
2090
2091 :ref:`faq_session_rollback`
2092
2093 :meth:`_orm.Session.in_transaction`
2094
2095
2096 """ # noqa: E501
2097
2098 return self._proxied.is_active
2099
2100 @property
2101 def autoflush(self) -> bool:
2102 r"""Proxy for the :attr:`_orm.Session.autoflush` attribute
2103 on behalf of the :class:`_orm.scoping.scoped_session` class.
2104
2105 """ # noqa: E501
2106
2107 return self._proxied.autoflush
2108
2109 @autoflush.setter
2110 def autoflush(self, attr: bool) -> None:
2111 self._proxied.autoflush = attr
2112
2113 @property
2114 def no_autoflush(self) -> Any:
2115 r"""Return a context manager that disables autoflush.
2116
2117 .. container:: class_bases
2118
2119 Proxied for the :class:`_orm.Session` class
2120 on behalf of the :class:`_orm.scoping.scoped_session` class.
2121
2122 e.g.::
2123
2124 with session.no_autoflush:
2125
2126 some_object = SomeClass()
2127 session.add(some_object)
2128 # won't autoflush
2129 some_object.related_thing = session.query(SomeRelated).first()
2130
2131 Operations that proceed within the ``with:`` block
2132 will not be subject to flushes occurring upon query
2133 access. This is useful when initializing a series
2134 of objects which involve existing database queries,
2135 where the uncompleted object should not yet be flushed.
2136
2137
2138 """ # noqa: E501
2139
2140 return self._proxied.no_autoflush
2141
2142 @property
2143 def info(self) -> Any:
2144 r"""A user-modifiable dictionary.
2145
2146 .. container:: class_bases
2147
2148 Proxied for the :class:`_orm.Session` class
2149 on behalf of the :class:`_orm.scoping.scoped_session` class.
2150
2151 The initial value of this dictionary can be populated using the
2152 ``info`` argument to the :class:`.Session` constructor or
2153 :class:`.sessionmaker` constructor or factory methods. The dictionary
2154 here is always local to this :class:`.Session` and can be modified
2155 independently of all other :class:`.Session` objects.
2156
2157
2158 """ # noqa: E501
2159
2160 return self._proxied.info
2161
2162 @classmethod
2163 def object_session(cls, instance: object) -> Optional[Session]:
2164 r"""Return the :class:`.Session` to which an object belongs.
2165
2166 .. container:: class_bases
2167
2168 Proxied for the :class:`_orm.Session` class on
2169 behalf of the :class:`_orm.scoping.scoped_session` class.
2170
2171 This is an alias of :func:`.object_session`.
2172
2173
2174 """ # noqa: E501
2175
2176 return Session.object_session(instance)
2177
2178 @classmethod
2179 def identity_key(
2180 cls,
2181 class_: Optional[Type[Any]] = None,
2182 ident: Union[Any, Tuple[Any, ...]] = None,
2183 *,
2184 instance: Optional[Any] = None,
2185 row: Optional[Union[Row[Unpack[TupleAny]], RowMapping]] = None,
2186 identity_token: Optional[Any] = None,
2187 ) -> _IdentityKeyType[Any]:
2188 r"""Return an identity key.
2189
2190 .. container:: class_bases
2191
2192 Proxied for the :class:`_orm.Session` class on
2193 behalf of the :class:`_orm.scoping.scoped_session` class.
2194
2195 This is an alias of :func:`.util.identity_key`.
2196
2197
2198 """ # noqa: E501
2199
2200 return Session.identity_key(
2201 class_=class_,
2202 ident=ident,
2203 instance=instance,
2204 row=row,
2205 identity_token=identity_token,
2206 )
2207
2208 # END PROXY METHODS scoped_session
2209
2210
2211ScopedSession = scoped_session
2212"""Old name for backwards compatibility."""