1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
|
==============
Session Basics
==============
What does the Session do ?
==========================
In the most general sense, the :class:`~.Session` establishes all conversations
with the database and represents a "holding zone" for all the objects which
you've loaded or associated with it during its lifespan. It provides the
interface where SELECT and other queries are made that will return and modify
ORM-mapped objects. The ORM objects themselves are maintained inside the
:class:`.Session`, inside a structure called the :term:`identity map` - a data
structure that maintains unique copies of each object, where "unique" means
"only one object with a particular primary key".
The :class:`.Session` begins in a mostly stateless form. Once queries are
issued or other objects are persisted with it, it requests a connection
resource from an :class:`_engine.Engine` that is associated with the
:class:`.Session`, and then establishes a transaction on that connection. This
transaction remains in effect until the :class:`.Session` is instructed to
commit or roll back the transaction.
The ORM objects maintained by a :class:`_orm.Session` are :term:`instrumented`
such that whenever an attribute or a collection is modified in the Python
program, a change event is generated which is recorded by the
:class:`_orm.Session`. Whenever the database is about to be queried, or when
the transaction is about to be committed, the :class:`_orm.Session` first
**flushes** all pending changes stored in memory to the database. This is
known as the :term:`unit of work` pattern.
When using a :class:`.Session`, it's useful to consider the ORM mapped objects
that it maintains as **proxy objects** to database rows, which are local to the
transaction being held by the :class:`.Session`. In order to maintain the
state on the objects as matching what's actually in the database, there are a
variety of events that will cause objects to re-access the database in order to
keep synchronized. It is possible to "detach" objects from a
:class:`.Session`, and to continue using them, though this practice has its
caveats. It's intended that usually, you'd re-associate detached objects with
another :class:`.Session` when you want to work with them again, so that they
can resume their normal task of representing database state.
.. _session_basics:
Basics of Using a Session
=========================
The most basic :class:`.Session` use patterns are presented here.
.. _session_getting:
Opening and Closing a Session
-----------------------------
The :class:`_orm.Session` may be constructed on its own or by using the
:class:`_orm.sessionmaker` class. It typically is passed a single
:class:`_engine.Engine` as a source of connectivity up front. A typical use
may look like::
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql://scott:tiger@localhost/")
# create session and add objects
with Session(engine) as session:
session.add(some_object)
session.add(some_other_object)
session.commit()
Above, the :class:`_orm.Session` is instantiated with an :class:`_engine.Engine`
associated with a particular database URL. It is then used in a Python
context manager (i.e. ``with:`` statement) so that it is automatically
closed at the end of the block; this is equivalent
to calling the :meth:`_orm.Session.close` method.
The call to :meth:`_orm.Session.commit` is optional, and is only needed if the
work we've done with the :class:`_orm.Session` includes new data to be
persisted to the database. If we were only issuing SELECT calls and did not
need to write any changes, then the call to :meth:`_orm.Session.commit` would
be unnecessary.
.. note::
Note that after :meth:`_orm.Session.commit` is called, either explicitly or
when using a context manager, all objects associated with the
:class:`.Session` are :term:`expired`, meaning their contents are erased to
be re-loaded within the next transaction. If these objects are instead
:term:`detached`, they will be non-functional until re-associated with a
new :class:`.Session`, unless the :paramref:`.Session.expire_on_commit`
parameter is used to disable this behavior. See the
section :ref:`session_committing` for more detail.
.. _session_begin_commit_rollback_block:
Framing out a begin / commit / rollback block
-----------------------------------------------
We may also enclose the :meth:`_orm.Session.commit` call and the overall
"framing" of the transaction within a context manager for those cases where
we will be committing data to the database. By "framing" we mean that if all
operations succeed, the :meth:`_orm.Session.commit` method will be called,
but if any exceptions are raised, the :meth:`_orm.Session.rollback` method
will be called so that the transaction is rolled back immediately, before
propagating the exception outward. In Python this is most fundamentally
expressed using a ``try: / except: / else:`` block such as::
# verbose version of what a context manager will do
with Session(engine) as session:
session.begin()
try:
session.add(some_object)
session.add(some_other_object)
except:
session.rollback()
raise
else:
session.commit()
The long-form sequence of operations illustrated above can be
achieved more succinctly by making use of the
:class:`_orm.SessionTransaction` object returned by the :meth:`_orm.Session.begin`
method, which provides a context manager interface for the same sequence of
operations::
# create session and add objects
with Session(engine) as session:
with session.begin():
session.add(some_object)
session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()
More succinctly, the two contexts may be combined::
# create session and add objects
with Session(engine) as session, session.begin():
session.add(some_object)
session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()
Using a sessionmaker
--------------------
The purpose of :class:`_orm.sessionmaker` is to provide a factory for
:class:`_orm.Session` objects with a fixed configuration. As it is typical
that an application will have an :class:`_engine.Engine` object in module
scope, the :class:`_orm.sessionmaker` can provide a factory for
:class:`_orm.Session` objects that are against this engine::
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# an Engine, which the Session will use for connection
# resources, typically in module scope
engine = create_engine("postgresql://scott:tiger@localhost/")
# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)
# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
session.add(some_object)
session.add(some_other_object)
session.commit()
# closes the session
The :class:`_orm.sessionmaker` is analogous to the :class:`_engine.Engine`
as a module-level factory for function-level sessions / connections. As such
it also has its own :meth:`_orm.sessionmaker.begin` method, analogous
to :meth:`_engine.Engine.begin`, which returns a :class:`_orm.Session` object
and also maintains a begin/commit/rollback block::
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql://scott:tiger@localhost/")
# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)
# we can now construct a Session() and include begin()/commit()/rollback()
# at once
with Session.begin() as session:
session.add(some_object)
session.add(some_other_object)
# commits the transaction, closes the session
Where above, the :class:`_orm.Session` will both have its transaction committed
as well as that the :class:`_orm.Session` will be closed, when the above
``with:`` block ends.
When you write your application, the
:class:`.sessionmaker` factory should be scoped the same as the
:class:`_engine.Engine` object created by :func:`_sa.create_engine`, which
is typically at module-level or global scope. As these objects are both
factories, they can be used by any number of functions and threads
simultaneously.
.. seealso::
:class:`_orm.sessionmaker`
:class:`_orm.Session`
.. _session_querying_1x:
Querying (1.x Style)
--------------------
The :meth:`~.Session.query` function takes one or more
**entities** and returns a new :class:`~sqlalchemy.orm.query.Query` object which
will issue mapper queries within the context of this Session. By
"entity" we refer to a mapped class, an attribute of a mapped class, or
other ORM constructs such as an :func:`_orm.aliased` construct::
# query from a class
results = session.query(User).filter_by(name="ed").all()
# query with multiple classes, returns tuples
results = session.query(User, Address).join("addresses").filter_by(name="ed").all()
# query using orm-columns, also returns tuples
results = session.query(User.name, User.fullname).all()
When ORM objects are returned in results, they are also stored in the identity
map. When an incoming database row has a primary key that matches an object
which is already present, the same object is returned, and those attributes
of the object which already have a value are not re-populated.
The :class:`_orm.Session` automatically expires all instances along transaction
boundaries (i.e. when the current transaction is committed or rolled back) so
that with a normally isolated transaction, data will refresh itself when a new
transaction begins.
The :class:`_query.Query` object is introduced in great detail in
:ref:`ormtutorial_toplevel`, and further documented in
:ref:`query_api_toplevel`.
.. seealso::
:ref:`ormtutorial_toplevel`
:meth:`_orm.Session.query`
:ref:`query_api_toplevel`
.. _session_querying_20:
Querying (2.0 style)
--------------------
.. versionadded:: 1.4
SQLAlchemy 2.0 will standardize the production of SELECT statements across both
Core and ORM by making direct use of the :class:`_sql.Select` object within the
ORM, removing the need for there to be a separate :class:`_orm.Query`
object. This mode of operation is available in SQLAlchemy 1.4 right now to
support applications that will be migrating to 2.0. The :class:`_orm.Session`
must be instantiated with the
:paramref:`_orm.Session.future` flag set to ``True``; from that point on the
:meth:`_orm.Session.execute` method will return ORM results via the
standard :class:`_engine.Result` object when invoking :func:`_sql.select`
statements that use ORM entities::
from sqlalchemy import select
from sqlalchemy.orm import Session
session = Session(engine, future=True)
# query from a class
statement = select(User).filter_by(name="ed")
# list of first element of each row (i.e. User objects)
result = session.execute(statement).scalars().all()
# query with multiple classes
statement = select(User, Address).join("addresses").filter_by(name="ed")
# list of tuples
result = session.execute(statement).all()
# query with ORM columns
statement = select(User.name, User.fullname)
# list of tuples
result = session.execute(statement).all()
It's important to note that while methods of :class:`_query.Query` such as
:meth:`_query.Query.all` and :meth:`_query.Query.one` will return instances
of ORM mapped objects directly in the case that only a single complete
entity were requested, the :class:`_engine.Result` object returned
by :meth:`_orm.Session.execute` will always deliver rows (named tuples)
by default; this is so that results against single or multiple ORM objects,
columns, tables, etc. may all be handled identically.
If only one ORM entity was queried, the rows returned will have exactly one
column, consisting of the ORM-mapped object instance for each row. To convert
these rows into object instances without the tuples, the
:meth:`_engine.Result.scalars` method is used to first apply a "scalars" filter
to the result; then the :class:`_engine.Result` can be iterated or deliver rows
via standard methods such as :meth:`_engine.Result.all`,
:meth:`_engine.Result.first`, etc.
.. seealso::
:ref:`migration_20_toplevel`
.. _session_adding:
Adding New or Existing Items
----------------------------
:meth:`~.Session.add` is used to place instances in the
session. For :term:`transient` (i.e. brand new) instances, this will have the effect
of an INSERT taking place for those instances upon the next flush. For
instances which are :term:`persistent` (i.e. were loaded by this session), they are
already present and do not need to be added. Instances which are :term:`detached`
(i.e. have been removed from a session) may be re-associated with a session
using this method::
user1 = User(name="user1")
user2 = User(name="user2")
session.add(user1)
session.add(user2)
session.commit() # write changes to the database
To add a list of items to the session at once, use
:meth:`~.Session.add_all`::
session.add_all([item1, item2, item3])
The :meth:`~.Session.add` operation **cascades** along
the ``save-update`` cascade. For more details see the section
:ref:`unitofwork_cascades`.
.. _session_deleting:
Deleting
--------
The :meth:`~.Session.delete` method places an instance
into the Session's list of objects to be marked as deleted::
# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)
# commit (or flush)
session.commit()
:meth:`_orm.Session.delete` marks an object for deletion, which will
result in a DELETE statement emitted for each primary key affected.
Before the pending deletes are flushed, objects marked by "delete" are present
in the :attr:`_orm.Session.deleted` collection. After the DELETE, they
are expunged from the :class:`_orm.Session`, which becomes permanent after
the transaction is committed.
There are various important behaviors related to the
:meth:`_orm.Session.delete` operation, particularly in how relationships to
other objects and collections are handled. There's more information on how
this works in the section :ref:`unitofwork_cascades`, but in general
the rules are:
* Rows that correspond to mapped objects that are related to a deleted
object via the :func:`_orm.relationship` directive are **not
deleted by default**. If those objects have a foreign key constraint back
to the row being deleted, those columns are set to NULL. This will
cause a constraint violation if the columns are non-nullable.
* To change the "SET NULL" into a DELETE of a related object's row, use the
:ref:`cascade_delete` cascade on the :func:`_orm.relationship`.
* Rows that are in tables linked as "many-to-many" tables, via the
:paramref:`_orm.relationship.secondary` parameter, **are** deleted in all
cases when the object they refer to is deleted.
* When related objects include a foreign key constraint back to the object
being deleted, and the related collections to which they belong are not
currently loaded into memory, the unit of work will emit a SELECT to fetch
all related rows, so that their primary key values can be used to emit either
UPDATE or DELETE statements on those related rows. In this way, the ORM
without further instruction will perform the function of ON DELETE CASCADE,
even if this is configured on Core :class:`_schema.ForeignKeyConstraint`
objects.
* The :paramref:`_orm.relationship.passive_deletes` parameter can be used
to tune this behavior and rely upon "ON DELETE CASCADE" more naturally;
when set to True, this SELECT operation will no longer take place, however
rows that are locally present will still be subject to explicit SET NULL
or DELETE. Setting :paramref:`_orm.relationship.passive_deletes` to
the string ``"all"`` will disable **all** related object update/delete.
* When the DELETE occurs for an object marked for deletion, the object
is not automatically removed from collections or object references that
refer to it. When the :class:`_orm.Session` is expired, these collections
may be loaded again so that the object is no longer present. However,
it is preferable that instead of using :meth:`_orm.Session.delete` for
these objects, the object should instead be removed from its collection
and then :ref:`cascade_delete_orphan` should be used so that it is
deleted as a secondary effect of that collection removal. See the
section :ref:`session_deleting_from_collections` for an example of this.
.. seealso::
:ref:`cascade_delete` - describes "delete cascade", which marks related
objects for deletion when a lead object is deleted.
:ref:`cascade_delete_orphan` - describes "delete orphan cascade", which
marks related objects for deletion when they are de-associated from their
lead object.
:ref:`session_deleting_from_collections` - important background on
:meth:`_orm.Session.delete` as involves relationships being refreshed
in memory.
.. _session_flushing:
Flushing
--------
When the :class:`~sqlalchemy.orm.session.Session` is used with its default
configuration, the flush step is nearly always done transparently.
Specifically, the flush occurs before any individual
SQL statement is issued as a result of a :class:`_query.Query` or
a :term:`2.0-style` :meth:`_orm.Session.execute` call, as well as within the
:meth:`~.Session.commit` call before the transaction is
committed. It also occurs before a SAVEPOINT is issued when
:meth:`~.Session.begin_nested` is used.
A :class:`.Session` flush can be forced at any time by calling the
:meth:`~.Session.flush` method::
session.flush()
The flush which occurs automatically within the scope of certain methods
is known as **autoflush**. Autoflush is defined as a configurable,
automatic flush call which occurs at the beginning of methods including:
* :meth:`_orm.Session.execute` and other SQL-executing methods
* When a :class:`_query.Query` is invoked to send SQL to the database
* Within the :meth:`.Session.merge` method before querying the database
* When objects are :ref:`refreshed <session_expiring>`
* When ORM :term:`lazy load` operations occur against unloaded object
attributes.
There are also points at which flushes occur **unconditionally**; these
points are within key transactional boundaries which include:
* Within the process of the :meth:`.Session.commit` method
* When :meth:`.Session.begin_nested` is called
* When the :meth:`.Session.prepare` 2PC method is used.
The **autoflush** behavior, as applied to the previous list of items,
can be disabled by constructing a :class:`.Session` or
:class:`.sessionmaker` passing the :paramref:`.Session.autoflush` parameter as
``False``::
Session = sessionmaker(autoflush=False)
Additionally, autoflush can be temporarily disabled within the flow
of using a :class:`.Session` using the
:attr:`.Session.no_autoflush` context manager::
with mysession.no_autoflush:
mysession.add(some_object)
mysession.flush()
**To reiterate:** The flush process **always occurs** when transactional
methods such as :meth:`.Session.commit` and :meth:`.Session.begin_nested` are
called, regardless of any "autoflush" settings, when the :class:`.Session` has
remaining pending changes to process.
The flush process *always* occurs within a transaction, (subject to the
:ref:`isolation level <session_transaction_isolation>` of the database
transaction), provided that the DBAPI is not in
:ref:`driver level autocommit <dbapi_autocommit>` mode. This includes even if
the :class:`~sqlalchemy.orm.session.Session` has been configured with the
deprecated :paramref:`_orm.Session.autocommit` setting, which disables the
session's persistent transactional state. If no transaction is present,
:meth:`~.Session.flush` creates its own transaction and commits it. This means
that assuming the database connection is providing for :term:`atomicity` within
its transactional settings, if any individual DML statement inside the flush
fails, the entire operation will be rolled back.
Outside of using :paramref:`_orm.Session.autocommit`, when a failure occurs
within a flush, in order to continue using that same :class:`_orm.Session`, an
explicit call to :meth:`~.Session.rollback` is required after a flush fails,
even though the underlying transaction will have been rolled back already (even
if the database driver is technically in driver-level autocommit mode). This is
so that the overall nesting pattern of so-called "subtransactions" is
consistently maintained. The FAQ section :ref:`faq_session_rollback` contains a
more detailed description of this behavior.
.. _session_get:
Get by Primary Key
------------------
As the :class:`_orm.Session` makes use of an :term:`identity map` which refers
to current in-memory objects by primary key, the :meth:`_orm.Session.get`
method is provided as a means of locating objects by primary key, first
looking within the current identity map and then querying the database
for non present values. Such as, to locate a ``User`` entity with primary key
identity ``(5, )``::
my_user = session.get(User, 5)
The :meth:`_orm.Session.get` also includes calling forms for composite primary
key values, which may be passed as tuples or dictionaries, as well as
additional parameters which allow for specific loader and execution options.
See :meth:`_orm.Session.get` for the complete parameter list.
.. seealso::
:meth:`_orm.Session.get`
.. _session_expiring:
Expiring / Refreshing
---------------------
An important consideration that will often come up when using the
:class:`_orm.Session` is that of dealing with the state that is present on
objects that have been loaded from the database, in terms of keeping them
synchronized with the current state of the transaction. The SQLAlchemy
ORM is based around the concept of an :term:`identity map` such that when
an object is "loaded" from a SQL query, there will be a unique Python
object instance maintained corresponding to a particular database identity.
This means if we emit two separate queries, each for the same row, and get
a mapped object back, the two queries will have returned the same Python
object::
>>> u1 = session.query(User).filter(id=5).first()
>>> u2 = session.query(User).filter(id=5).first()
>>> u1 is u2
True
Following from this, when the ORM gets rows back from a query, it will
**skip the population of attributes** for an object that's already loaded.
The design assumption here is to assume a transaction that's perfectly
isolated, and then to the degree that the transaction isn't isolated, the
application can take steps on an as-needed basis to refresh objects
from the database transaction. The FAQ entry at :ref:`faq_session_identity`
discusses this concept in more detail.
When an ORM mapped object is loaded into memory, there are three general
ways to refresh its contents with new data from the current transaction:
* **the expire() method** - the :meth:`_orm.Session.expire` method will
erase the contents of selected or all attributes of an object, such that they
will be loaded from the database when they are next accessed, e.g. using
a :term:`lazy loading` pattern::
session.expire(u1)
u1.some_attribute # <-- lazy loads from the transaction
..
* **the refresh() method** - closely related is the :meth:`_orm.Session.refresh`
method, which does everything the :meth:`_orm.Session.expire` method does
but also emits one or more SQL queries immediately to actually refresh
the contents of the object::
session.refresh(u1) # <-- emits a SQL query
u1.some_attribute # <-- is refreshed from the transaction
..
* **the populate_existing() method or execution option** - This is now
an execution option documented at :ref:`orm_queryguide_populate_existing`; in
legacy form it's found on the :class:`_orm.Query` object as the
:meth:`_orm.Query.populate_existing` method. This operation in either form
indicates that objects being returned from a query should be unconditionally
re-populated from their contents in the database::
u2 = session.query(User).populate_existing().filter(id=5).first()
..
Further discussion on the refresh / expire concept can be found at
:ref:`session_expire`.
.. seealso::
:ref:`session_expire`
:ref:`faq_session_identity`
.. _orm_expression_update_delete:
UPDATE and DELETE with arbitrary WHERE clause
---------------------------------------------
The sections above on :meth:`_orm.Session.flush` and :meth:`_orm.Session.delete`
detail how rows can be inserted, updated and deleted in the database,
based on primary key identities that are referred towards by mapped Python
objects in the application. The :class:`_orm.Session` can also emit UPDATE
and DELETE statements with arbitrary WHERE clauses as well, and at the same
time refresh locally present objects which match those rows.
To emit an ORM-enabled UPDATE in :term:`1.x style`, the :meth:`_query.Query.update` method
may be used::
session.query(User).filter(User.name == "squidward").update(
{"name": "spongebob"}, synchronize_session="fetch"
)
Above, an UPDATE will be emitted against all rows that match the name
"squidward" and be updated to the name "spongebob". The
:paramref:`_query.Query.update.synchronize_session` parameter referring to
"fetch" indicates the list of affected primary keys should be fetched either
via a separate SELECT statement or via RETURNING if the backend database supports it;
objects locally present in memory will be updated in memory based on these
primary key identities.
For ORM-enabled UPDATEs in :term:`2.0 style`, :meth:`_orm.Session.execute` is used with the
Core :class:`_sql.Update` construct::
from sqlalchemy import update
stmt = (
update(User)
.where(User.name == "squidward")
.values(name="spongebob")
.execution_options(synchronize_session="fetch")
)
result = session.execute(stmt)
Above, the :meth:`_dml.Update.execution_options` method may be used to
establish execution-time options such as "synchronize_session".
The result object returned is an instance of :class:`_result.CursorResult`; to
retrieve the number of rows matched by any UPDATE or DELETE statement, use
:attr:`_result.CursorResult.rowcount`::
num_rows_matched = result.rowcount
DELETEs work in the same way as UPDATE except there is no "values / set"
clause established. When synchronize_session is used, matching objects
within the :class:`_orm.Session` will be marked as deleted and expunged.
ORM-enabled delete, :term:`1.x style`::
session.query(User).filter(User.name == "squidward").delete(synchronize_session="fetch")
ORM-enabled delete, :term:`2.0 style`::
from sqlalchemy import delete
stmt = (
delete(User)
.where(User.name == "squidward")
.execution_options(synchronize_session="fetch")
)
session.execute(stmt)
.. _orm_expression_update_delete_sync:
Selecting a Synchronization Strategy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
With both the 1.x and 2.0 form of ORM-enabled updates and deletes, the following
values for ``synchronize_session`` are supported:
* ``False`` - don't synchronize the session. This option is the most
efficient and is reliable once the session is expired, which
typically occurs after a commit(), or explicitly using
expire_all(). Before the expiration, objects that were updated or deleted
in the database may still
remain in the session with stale values, which
can lead to confusing results.
* ``'fetch'`` - Retrieves the primary key identity of affected rows by either
performing a SELECT before the UPDATE or DELETE, or by using RETURNING if the
database supports it, so that in-memory objects which are affected by the
operation can be refreshed with new values (updates) or expunged from the
:class:`_orm.Session` (deletes). Note that this synchronization strategy is
not available if the given :func:`_dml.update` or :func:`_dml.delete`
construct specifies columns for :meth:`_dml.UpdateBase.returning` explicitly.
* ``'evaluate'`` - Evaluate the WHERE criteria given in the UPDATE or DELETE
statement in Python, to locate matching objects within the
:class:`_orm.Session`. This approach does not add any round trips and in
the absence of RETURNING support is more efficient. For UPDATE or DELETE
statements with complex criteria, the ``'evaluate'`` strategy may not be
able to evaluate the expression in Python and will raise an error. If
this occurs, use the ``'fetch'`` strategy for the operation instead.
.. warning::
The ``"evaluate"`` strategy should be avoided if an UPDATE operation is
to run on a :class:`_orm.Session` that has many objects which have
been expired, because it will necessarily need to refresh those objects
as they are located which will emit a SELECT for each one. The
:class:`_orm.Session` may have expired objects if it is being used
across multiple :meth:`_orm.Session.commit` calls and the
:paramref:`_orm.Session.expire_on_commit` flag is at its default
value of ``True``.
.. warning:: **Additional Caveats for ORM-enabled updates and deletes**
The ORM-enabled UPDATE and DELETE features bypass ORM unit-of-work
automation in favor being able to emit a single UPDATE or DELETE statement
that matches multiple rows at once without complexity.
* The operations do not offer in-Python cascading of
relationships - it is assumed that ON UPDATE CASCADE and/or
ON DELETE CASCADE is
configured for any foreign key references which require
it, otherwise the database may emit an integrity
violation if foreign key references are being enforced.
* After the UPDATE or DELETE, dependent objects in the
:class:`.Session` which were impacted by an ON UPDATE CASCADE or ON
DELETE CASCADE on related tables may not contain the current state;
this issue is resolved once the :class:`.Session` is expired, which
normally occurs upon :meth:`.Session.commit` or can be forced by
using
:meth:`.Session.expire_all`.
* The ``'fetch'`` strategy, when run on a database that does not support
RETURNING such as MySQL or SQLite, results in an additional SELECT
statement emitted which may reduce performance. Use SQL echoing when
developing to evaluate the impact of SQL emitted.
* ORM-enabled UPDATEs and DELETEs do not handle joined table inheritance
automatically. If the operation is against multiple tables, typically
individual UPDATE / DELETE statements against the individual tables
should be used. Some databases support multiple table UPDATEs.
Similar guidelines as those detailed at :ref:`tutorial_update_from`
may be applied.
* The WHERE criteria needed in order to limit the polymorphic identity to
specific subclasses for single-table-inheritance mappings **is included
automatically** . This only applies to a subclass mapper that has no
table of its own.
.. versionchanged:: 1.4 ORM updates/deletes now automatically
accommodate for the WHERE criteria added for single-inheritance
mappings.
* The :func:`_orm.with_loader_criteria` option **is supported** by ORM
update and delete operations; criteria here will be added to that of the
UPDATE or DELETE statement being emitted, as well as taken into account
during the "synchronize" process.
* In order to intercept ORM-enabled UPDATE and DELETE operations with event
handlers, use the :meth:`_orm.SessionEvents.do_orm_execute` event.
Selecting ORM Objects Inline with UPDATE.. RETURNING or INSERT..RETURNING
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This section has moved. See :ref:`orm_dml_returning_objects`.
.. _session_autobegin:
Auto Begin
----------
.. versionadded:: 1.4
This section describes a behavior that is new in SQLAlchemy 1.4 and does
not apply to previous versions. Further details on the "autobegin"
change are at :ref:`change_5074`.
The :class:`_orm.Session` object features a behavior known as **autobegin**.
This indicates that the :class:`_orm.Session` will internally consider itself
to be in a "transactional" state as soon as any work is performed with the
:class:`_orm.Session`, either involving modifications to the internal state of
the :class:`_orm.Session` with regards to object state changes, or with
operations that require database connectivity.
When the :class:`_orm.Session` is first constructed, there's no transactional
state present. The transactional state is begun automatically, when
a method such as :meth:`_orm.Session.add` or :meth:`_orm.Session.execute`
is invoked, or similarly if a :class:`_orm.Query` is executed to return
results (which ultimately uses :meth:`_orm.Session.execute`), or if
an attribute is modified on a :term:`persistent` object.
The transactional state can be checked by accessing the
:meth:`_orm.Session.in_transaction` method, which returns ``True`` or ``False``
indicating if the "autobegin" step has proceeded. While not normally needed,
the :meth:`_orm.Session.get_transaction` method will return the actual
:class:`_orm.SessionTransaction` object that represents this transactional
state.
The transactional state of the :class:`_orm.Session` may also be started
explicitly, by invoking the :meth:`_orm.Session.begin` method. When this
method is called, the :class:`_orm.Session` is placed into the "transactional"
state unconditionally. :meth:`_orm.Session.begin` may be used as a context
manager as described at :ref:`session_begin_commit_rollback_block`.
.. versionchanged:: 1.4.12 - autobegin now correctly occurs if object
attributes are modified; previously this was not occurring.
.. _session_committing:
Committing
----------
:meth:`~.Session.commit` is used to commit the current
transaction. At its core this indicates that it emits ``COMMIT`` on
all current database connections that have a transaction in progress;
from a :term:`DBAPI` perspective this means the ``connection.commit()``
DBAPI method is invoked on each DBAPI connection.
When there is no transaction in place for the :class:`.Session`, indicating
that no operations were invoked on this :class:`.Session` since the previous
call to :meth:`.Session.commit`, the method will begin and commit an
internal-only "logical" transaction, that does not normally affect the database
unless pending flush changes were detected, but will still invoke event
handlers and object expiration rules.
The :meth:`_orm.Session.commit` operation unconditionally issues
:meth:`~.Session.flush` before emitting COMMIT on relevant database
connections. If no pending changes are detected, then no SQL is emitted to the
database. This behavior is not configurable and is not affected by the
:paramref:`.Session.autoflush` parameter.
Subsequent to that, :meth:`_orm.Session.commit` will then COMMIT the actual
database transaction or transactions, if any, that are in place.
Finally, all objects within the :class:`_orm.Session` are :term:`expired` as
the transaction is closed out. This is so that when the instances are next
accessed, either through attribute access or by them being present in the
result of a SELECT, they receive the most recent state. This behavior may be
controlled by the :paramref:`_orm.Session.expire_on_commit` flag, which may be
set to ``False`` when this behavior is undesirable.
.. seealso::
:ref:`session_autobegin`
.. _session_rollback:
Rolling Back
------------
:meth:`~.Session.rollback` rolls back the current transaction, if any.
When there is no transaction in place, the method passes silently.
With a default configured session, the
post-rollback state of the session, subsequent to a transaction having
been begun either via :ref:`autobegin <session_autobegin>`
or by calling the :meth:`_orm.Session.begin`
method explicitly, is as follows:
* All transactions are rolled back and all connections returned to the
connection pool, unless the Session was bound directly to a Connection, in
which case the connection is still maintained (but still rolled back).
* Objects which were initially in the :term:`pending` state when they were added
to the :class:`~sqlalchemy.orm.session.Session` within the lifespan of the
transaction are expunged, corresponding to their INSERT statement being
rolled back. The state of their attributes remains unchanged.
* Objects which were marked as :term:`deleted` within the lifespan of the
transaction are promoted back to the :term:`persistent` state, corresponding to
their DELETE statement being rolled back. Note that if those objects were
first :term:`pending` within the transaction, that operation takes precedence
instead.
* All objects not expunged are fully expired - this is regardless of the
:paramref:`_orm.Session.expire_on_commit` setting.
With that state understood, the :class:`_orm.Session` may
safely continue usage after a rollback occurs.
.. versionchanged:: 1.4
The :class:`_orm.Session` object now features deferred "begin" behavior, as
described in :ref:`autobegin <session_autobegin>`. If no transaction is
begun, methods like :meth:`_orm.Session.commit` and
:meth:`_orm.Session.rollback` have no effect. This behavior would not
have been observed prior to 1.4 as under non-autocommit mode, a
transaction would always be implicitly present.
When a :meth:`_orm.Session.flush` fails, typically for reasons like primary
key, foreign key, or "not nullable" constraint violations, a ROLLBACK is issued
automatically (it's currently not possible for a flush to continue after a
partial failure). However, the :class:`_orm.Session` goes into a state known as
"inactive" at this point, and the calling application must always call the
:meth:`_orm.Session.rollback` method explicitly so that the
:class:`_orm.Session` can go back into a usable state (it can also be simply
closed and discarded). See the FAQ entry at :ref:`faq_session_rollback` for
further discussion.
.. seealso::
:ref:`session_autobegin`
.. _session_closing:
Closing
-------
The :meth:`~.Session.close` method issues a :meth:`~.Session.expunge_all` which
removes all ORM-mapped objects from the session, and :term:`releases` any
transactional/connection resources from the :class:`_engine.Engine` object(s)
to which it is bound. When connections are returned to the connection pool,
transactional state is rolled back as well.
When the :class:`_orm.Session` is closed, it is essentially in the
original state as when it was first constructed, and **may be used again**.
In this sense, the :meth:`_orm.Session.close` method is more like a "reset"
back to the clean state and not as much like a "database close" method.
It's recommended that the scope of a :class:`_orm.Session` be limited by
a call to :meth:`_orm.Session.close` at the end, especially if the
:meth:`_orm.Session.commit` or :meth:`_orm.Session.rollback` methods are not
used. The :class:`_orm.Session` may be used as a context manager to ensure
that :meth:`_orm.Session.close` is called::
with Session(engine) as session:
result = session.execute(select(User))
# closes session automatically
.. versionchanged:: 1.4
The :class:`_orm.Session` object features deferred "begin" behavior, as
described in :ref:`autobegin <session_autobegin>`. no longer immediately
begins a new transaction after the :meth:`_orm.Session.close` method is
called.
.. _session_faq:
Session Frequently Asked Questions
==================================
By this point, many users already have questions about sessions.
This section presents a mini-FAQ (note that we have also a :doc:`real FAQ </faq/index>`)
of the most basic issues one is presented with when using a :class:`.Session`.
When do I make a :class:`.sessionmaker`?
----------------------------------------
Just one time, somewhere in your application's global scope. It should be
looked upon as part of your application's configuration. If your
application has three .py files in a package, you could, for example,
place the :class:`.sessionmaker` line in your ``__init__.py`` file; from
that point on your other modules say "from mypackage import Session". That
way, everyone else just uses :class:`.Session()`,
and the configuration of that session is controlled by that central point.
If your application starts up, does imports, but does not know what
database it's going to be connecting to, you can bind the
:class:`.Session` at the "class" level to the
engine later on, using :meth:`.sessionmaker.configure`.
In the examples in this section, we will frequently show the
:class:`.sessionmaker` being created right above the line where we actually
invoke :class:`.Session`. But that's just for
example's sake! In reality, the :class:`.sessionmaker` would be somewhere
at the module level. The calls to instantiate :class:`.Session`
would then be placed at the point in the application where database
conversations begin.
.. _session_faq_whentocreate:
When do I construct a :class:`.Session`, when do I commit it, and when do I close it?
-------------------------------------------------------------------------------------
.. topic:: tl;dr;
1. As a general rule, keep the lifecycle of the session **separate and
external** from functions and objects that access and/or manipulate
database data. This will greatly help with achieving a predictable
and consistent transactional scope.
2. Make sure you have a clear notion of where transactions
begin and end, and keep transactions **short**, meaning, they end
at the series of a sequence of operations, instead of being held
open indefinitely.
A :class:`.Session` is typically constructed at the beginning of a logical
operation where database access is potentially anticipated.
The :class:`.Session`, whenever it is used to talk to the database,
begins a database transaction as soon as it starts communicating.
This transaction remains in progress until the :class:`.Session`
is rolled back, committed, or closed. The :class:`.Session` will
begin a new transaction if it is used again, subsequent to the previous
transaction ending; from this it follows that the :class:`.Session`
is capable of having a lifespan across many transactions, though only
one at a time. We refer to these two concepts as **transaction scope**
and **session scope**.
It's usually not very hard to determine the best points at which
to begin and end the scope of a :class:`.Session`, though the wide
variety of application architectures possible can introduce
challenging situations.
Some sample scenarios include:
* Web applications. In this case, it's best to make use of the SQLAlchemy
integrations provided by the web framework in use. Or otherwise, the
basic pattern is create a :class:`_orm.Session` at the start of a web
request, call the :meth:`_orm.Session.commit` method at the end of
web requests that do POST, PUT, or DELETE, and then close the session
at the end of web request. It's also usually a good idea to set
:paramref:`_orm.Session.expire_on_commit` to False so that subsequent
access to objects that came from a :class:`_orm.Session` within the
view layer do not need to emit new SQL queries to refresh the objects,
if the transaction has been committed already.
* A background daemon which spawns off child forks
would want to create a :class:`.Session` local to each child
process, work with that :class:`.Session` through the life of the "job"
that the fork is handling, then tear it down when the job is completed.
* For a command-line script, the application would create a single, global
:class:`.Session` that is established when the program begins to do its
work, and commits it right as the program is completing its task.
* For a GUI interface-driven application, the scope of the :class:`.Session`
may best be within the scope of a user-generated event, such as a button
push. Or, the scope may correspond to explicit user interaction, such as
the user "opening" a series of records, then "saving" them.
As a general rule, the application should manage the lifecycle of the
session *externally* to functions that deal with specific data. This is a
fundamental separation of concerns which keeps data-specific operations
agnostic of the context in which they access and manipulate that data.
E.g. **don't do this**::
### this is the **wrong way to do it** ###
class ThingOne(object):
def go(self):
session = Session()
try:
session.query(FooBar).update({"x": 5})
session.commit()
except:
session.rollback()
raise
class ThingTwo(object):
def go(self):
session = Session()
try:
session.query(Widget).update({"q": 18})
session.commit()
except:
session.rollback()
raise
def run_my_program():
ThingOne().go()
ThingTwo().go()
Keep the lifecycle of the session (and usually the transaction)
**separate and external**. The example below illustrates how this might look,
and additionally makes use of a Python context manager (i.e. the ``with:``
keyword) in order to manage the scope of the :class:`_orm.Session` and its
transaction automatically::
### this is a **better** (but not the only) way to do it ###
class ThingOne(object):
def go(self, session):
session.query(FooBar).update({"x": 5})
class ThingTwo(object):
def go(self, session):
session.query(Widget).update({"q": 18})
def run_my_program():
with Session() as session:
with session.begin():
ThingOne().go(session)
ThingTwo().go(session)
.. versionchanged:: 1.4 The :class:`_orm.Session` may be used as a context
manager without the use of external helper functions.
Is the Session a cache?
-----------------------
Yeee...no. It's somewhat used as a cache, in that it implements the
:term:`identity map` pattern, and stores objects keyed to their primary key.
However, it doesn't do any kind of query caching. This means, if you say
``session.query(Foo).filter_by(name='bar')``, even if ``Foo(name='bar')``
is right there, in the identity map, the session has no idea about that.
It has to issue SQL to the database, get the rows back, and then when it
sees the primary key in the row, *then* it can look in the local identity
map and see that the object is already there. It's only when you say
``query.get({some primary key})`` that the
:class:`~sqlalchemy.orm.session.Session` doesn't have to issue a query.
Additionally, the Session stores object instances using a weak reference
by default. This also defeats the purpose of using the Session as a cache.
The :class:`.Session` is not designed to be a
global object from which everyone consults as a "registry" of objects.
That's more the job of a **second level cache**. SQLAlchemy provides
a pattern for implementing second level caching using `dogpile.cache <https://dogpilecache.readthedocs.io/>`_,
via the :ref:`examples_caching` example.
How can I get the :class:`~sqlalchemy.orm.session.Session` for a certain object?
------------------------------------------------------------------------------------
Use the :meth:`~.Session.object_session` classmethod
available on :class:`~sqlalchemy.orm.session.Session`::
session = Session.object_session(someobject)
The newer :ref:`core_inspection_toplevel` system can also be used::
from sqlalchemy import inspect
session = inspect(someobject).session
.. _session_faq_threadsafe:
Is the session thread-safe?
---------------------------
The :class:`.Session` is very much intended to be used in a
**non-concurrent** fashion, which usually means in only one thread at a
time.
The :class:`.Session` should be used in such a way that one
instance exists for a single series of operations within a single
transaction. One expedient way to get this effect is by associating
a :class:`.Session` with the current thread (see :ref:`unitofwork_contextual`
for background). Another is to use a pattern
where the :class:`.Session` is passed between functions and is otherwise
not shared with other threads.
The bigger point is that you should not *want* to use the session
with multiple concurrent threads. That would be like having everyone at a
restaurant all eat from the same plate. The session is a local "workspace"
that you use for a specific set of tasks; you don't want to, or need to,
share that session with other threads who are doing some other task.
Making sure the :class:`.Session` is only used in a single concurrent thread at a time
is called a "share nothing" approach to concurrency. But actually, not
sharing the :class:`.Session` implies a more significant pattern; it
means not just the :class:`.Session` object itself, but
also **all objects that are associated with that Session**, must be kept within
the scope of a single concurrent thread. The set of mapped
objects associated with a :class:`.Session` are essentially proxies for data
within database rows accessed over a database connection, and so just like
the :class:`.Session` itself, the whole
set of objects is really just a large-scale proxy for a database connection
(or connections). Ultimately, it's mostly the DBAPI connection itself that
we're keeping away from concurrent access; but since the :class:`.Session`
and all the objects associated with it are all proxies for that DBAPI connection,
the entire graph is essentially not safe for concurrent access.
If there are in fact multiple threads participating
in the same task, then you may consider sharing the session and its objects between
those threads; however, in this extremely unusual scenario the application would
need to ensure that a proper locking scheme is implemented so that there isn't
*concurrent* access to the :class:`.Session` or its state. A more common approach
to this situation is to maintain a single :class:`.Session` per concurrent thread,
but to instead *copy* objects from one :class:`.Session` to another, often
using the :meth:`.Session.merge` method to copy the state of an object into
a new object local to a different :class:`.Session`.
|