* :class:`_sql.ColumnElement` objects more generally, which are the root
of all Core SQL Expression language column-level expressions
-* :class:`_orm.InstrumentedAttribute` objects, which are ORM level mapped
- attributes.
+* :class:`_orm.InstrumentedAttribute` objects, which are ORM
+ level mapped attributes.
The operators are first introduced in the tutorial sections, including:
cursor
A control structure that enables traversal over the records in a database.
- In the Python DBAPI, the cursor object in fact the starting point
+ In the Python DBAPI, the cursor object is in fact the starting point
for statement execution as well as the interface used for fetching
results.
.. _asyncio_toplevel:
-asyncio
-=======
+Asynchronous I/O (asyncio)
+==========================
Support for Python asyncio. Support for Core and ORM usage is
included, using asyncio-compatible dialects.
.. currentmodule:: sqlalchemy.orm
-.. autoclass:: sqlalchemy.orm.state.AttributeState
+.. autoclass:: AttributeState
:members:
-.. autoclass:: sqlalchemy.orm.util.CascadeOptions
+.. autoclass:: CascadeOptions
:members:
-.. autoclass:: sqlalchemy.orm.instrumentation.ClassManager
+.. autoclass:: ClassManager
:members:
:inherited-members:
-.. autoclass:: sqlalchemy.orm.ColumnProperty
+.. autoclass:: ColumnProperty
:members:
.. attribute:: Comparator.expressions
:ref:`maptojoin` - usage example
-.. autoclass:: sqlalchemy.orm.CompositeProperty
+.. autoclass:: CompositeProperty
:members:
-.. autoclass:: sqlalchemy.orm.attributes.Event
+.. autoclass:: AttributeEvent
:members:
-.. autoclass:: sqlalchemy.orm.identity.IdentityMap
+.. autoclass:: IdentityMap
:members:
-.. autoclass:: sqlalchemy.orm.base.InspectionAttr
+.. autoclass:: InspectionAttr
:members:
-.. autoclass:: sqlalchemy.orm.base.InspectionAttrInfo
+.. autoclass:: InspectionAttrInfo
:members:
-.. autoclass:: sqlalchemy.orm.state.InstanceState
+.. autoclass:: InstanceState
:members:
-.. autoclass:: sqlalchemy.orm.attributes.InstrumentedAttribute
+.. autoclass:: InstrumentedAttribute
:members: __get__, __set__, __delete__
:undoc-members:
-.. autodata:: sqlalchemy.orm.MANYTOONE
+.. autodata:: MANYTOONE
-.. autodata:: sqlalchemy.orm.MANYTOMANY
+.. autodata:: MANYTOMANY
-.. autoclass:: sqlalchemy.orm.MapperProperty
+.. autoclass:: MapperProperty
:members:
.. py:attribute:: info
:attr:`.SchemaItem.info`
-.. autodata:: sqlalchemy.orm.interfaces.NOT_EXTENSION
+.. autodata:: NOT_EXTENSION
-.. autofunction:: sqlalchemy.orm.loading.merge_result
+.. autofunction:: merge_result
-.. autofunction:: sqlalchemy.orm.loading.merge_frozen_result
+.. autofunction:: merge_frozen_result
-.. autodata:: sqlalchemy.orm.ONETOMANY
+.. autodata:: ONETOMANY
-.. autoclass:: sqlalchemy.orm.PropComparator
+.. autoclass:: PropComparator
:members:
:inherited-members:
-.. autoclass:: sqlalchemy.orm.RelationshipProperty
+.. autoclass:: RelationshipProperty
:members:
:inherited-members:
-.. autoclass:: sqlalchemy.orm.SynonymProperty
+.. autoclass:: SynonymProperty
:members:
:inherited-members:
-.. autoclass:: sqlalchemy.orm.query.QueryContext
+.. autoclass:: QueryContext
:members:
-.. autoclass:: sqlalchemy.orm.attributes.QueryableAttribute
+.. autoclass:: QueryableAttribute
:members:
:inherited-members:
-.. autoclass:: sqlalchemy.orm.session.UOWTransaction
+.. autoclass:: UOWTransaction
:members:
spongebob Spongebob Squarepants
-
.. _orm_queryguide_select_columns:
Selecting ORM Entities and Attributes
A :class:`_sql.Select` object that contains ORM-annotated entities is normally
executed using a :class:`_orm.Session` object, and not a :class:`_future.Connection`
-object, so that ORM-related features may take effect.
+object, so that ORM-related features may take effect, including that
+instances of ORM-mapped objects may be returned. When using the
+:class:`_future.Connection` directly, result rows will only contain
+column-level data.
Below we select from the ``User`` entity, producing a :class:`_sql.Select`
that selects from the mapped :class:`_schema.Table` to which ``User`` is mapped::
ORDER BY user_account.id, address.id
[...] (){stop}
-ORM attributes, themselves known as :class:`_orm.InstrumentedAttribute`
+ORM attributes, themselves known as
+:class:`_orm.InstrumentedAttribute`
objects, can be used in the same way as any :class:`_sql.ColumnElement`,
and are delivered in result rows just the same way, such as below
where we refer to their values by column name within each row::
JOIN order_items AS order_items_1 ON user_order.id = order_items_1.order_id
JOIN item ON item.id = order_items_1.item_id
-.. tip::
+The order in which each call to the :meth:`_sql.Select.join` method
+is significant only to the degree that the "left" side of what we would like
+to join from needs to be present in the list of FROMs before we indicate a
+new target. :meth:`_sql.Select.join` would not, for example, know how to
+join correctly if we were to specify
+``select(User).join(Order.items).join(User.orders)``, and would raise an
+error. In correct practice, the :meth:`_sql.Select.join` method is invoked
+in such a way that lines up with how we would want the JOIN clauses in SQL
+to be rendered, and each call should represent a clear link from what
+precedes it.
+
+All of the elements that we target in the FROM clause remain available
+as potential points to continue joining FROM. We can continue to add
+other elements to join FROM the ``User`` entity above, for example adding
+on the ``User.addresses`` relationship to our chain of joins::
+
+ >>> stmt = (
+ ... select(User).
+ ... join(User.orders).
+ ... join(Order.items).
+ ... join(User.addresses)
+ ... )
+ >>> print(stmt)
+ {opensql}SELECT user_account.id, user_account.name, user_account.fullname
+ FROM user_account
+ JOIN user_order ON user_account.id = user_order.user_id
+ JOIN order_items AS order_items_1 ON user_order.id = order_items_1.order_id
+ JOIN item ON item.id = order_items_1.item_id
+ JOIN address ON user_account.id = address.user_id
- as seen in the above example, **the order in which each call to the join()
- method occurs is important**. Query would not, for example, know how to
- join correctly if we were to specify ``User``, then ``Item``, then
- ``Order``, in our chain of joins; in such a case, depending on the
- arguments passed, it may raise an error that it doesn't know how to join,
- or it may produce invalid SQL in which case the database will raise an
- error. In correct practice, the :meth:`_sql.Select.join` method is invoked
- in such a way that lines up with how we would want the JOIN clauses in SQL
- to be rendered, and each call should represent a clear link from what
- precedes it.
Joins to a Target Entity or Selectable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``"address"`` to the :func:`_orm.aliased` construct so that we may
refer to it by name in the result row::
-
>>> address_subq = aliased(Address, subq, name="address")
>>> stmt = select(User, address_subq).join(address_subq)
>>> for row in session.execute(stmt):
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=3, email_address='squirrel@squirrelpower.org')
-
Controlling what to Join From
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
WHERE user_account.name = :name_1
-
-
Special Relationship Operators
------------------------------
.. _orm_queryguide_autoflush:
Autoflush
-^^^^^^^^^^
+^^^^^^^^^
This option when passed as ``False`` will cause the :class:`_orm.Session`
to not invoke the "autoflush" step. It's equivalent to using the
.. _orm_queryguide_yield_per:
Yield Per
-^^^^^^^^^^
+^^^^^^^^^
The ``yield_per`` execution option is an integer value which will cause the
:class:`_engine.Result` to yield only a fixed count of rows at a time. It is
When ``yield_per`` is used, the
:paramref:`_engine.Connection.execution_options.stream_results` option is also
set for the Core execution, so that a streaming / server side cursor will be
-used if the backend supports it (currently known are
-:mod:`~sqlalchemy.dialects.postgresql.psycopg2`,
-:mod:`~sqlalchemy.dialects.mysql.mysqldb` and
-:mod:`~sqlalchemy.dialects.mysql.pymysql`. Other backends will pre buffer all
-rows. The memory use of raw database rows is much less than that of an
-ORM-mapped object, but should still be taken into consideration when
-benchmarking.
+used if the backend supports it [1]_
-The ``yield_per`` execution option **is not compatible subqueryload eager
+The ``yield_per`` execution option **is not compatible with subqueryload eager
loading or joinedload eager loading when using collections**. It is
-potentially compatible with "select in" eager loading, **provided the database
-driver supports multiple, independent cursors** (pysqlite and psycopg2 are
-known to work, MySQL and SQL Server ODBC drivers do not).
+potentially compatible with selectinload eager loading, **provided the database
+driver supports multiple, independent cursors** [2]_ .
The ``yield_per`` execution option is equvialent to the
:meth:`_orm.Query.yield_per` method in :term:`1.x style` ORM queries.
-.. seealso::
+.. [1] currently known are
+ :mod:`_postgresql.psycopg2`,
+ :mod:`_mysql.mysqldb` and
+ :mod:`_mysql.pymysql`. Other backends will pre buffer
+ all rows. The memory use of raw database rows is much less than that of an
+ ORM-mapped object, but should still be taken into consideration when
+ benchmarking.
- :ref:`engine_stream_results`
+.. [2] the :mod:`_postgresql.psycopg2`
+ and :mod:`_sqlite.pysqlite` drivers are
+ known to work, drivers for MySQL and SQL Server ODBC drivers do not.
+.. seealso::
+ :ref:`engine_stream_results`
ORM Update / Delete with Arbitrary WHERE clause
matching objects locally present in the :class:`_orm.Session`. See the section
:ref:`orm_expression_update_delete` for background on this feature.
-
-
-
from sqlalchemy import update
- stmt = update(User).where(User.nane == "squidward").values(name="spongebob")).\
+ stmt = update(User).where(User.name == "squidward").values(name="spongebob")).\
execution_options(synchronize_session="fetch")
session.execute(stmt)
ORM-enabled delete, :term:`1.x style`::
- session.query(User).filter(User.nane == "squidward").\
+ 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.nane == "squidward").execution_options(synchronize_session="fetch")
+ stmt = delete(User).where(User.name == "squidward").execution_options(synchronize_session="fetch")
session.execute(stmt)
])
session.commit()
-Commit at once
-~~~~~~~~~~~~~~~~
+Begin Once
+~~~~~~~~~~
Both :class:`_orm.sessionmaker` and :class:`_future.Engine` feature a
:meth:`_future.Engine.begin` method that will both procure a new object
* :ref:`tutorial_core_insert` - to get some data into the database, we introduce
and demonstrate the Core :class:`_sql.Insert` construct. INSERTs from an
- ORM perspective are described later, at :ref:`tutorial_orm_data_manipulation`.
+ ORM perspective are described in the next section
+ :ref:`tutorial_orm_data_manipulation`.
* :ref:`tutorial_selecting_data` - this section will describe in detail
the :class:`_sql.Select` construct, which is the most commonly used object
in SQLAlchemy. The :class:`_sql.Select` construct emits SELECT statements
for both Core and ORM centric applications and both use cases will be
- described here. Additional ORM use cases are also noted in he later
+ described here. Additional ORM use cases are also noted in the later
section :ref:`tutorial_select_relationships` as well as the
:ref:`queryguide_toplevel`.
The insert() SQL Expression Construct
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-A simple example of :class:`_sql.Insert` illustrates the target table
+A simple example of :class:`_sql.Insert` illustrating the target table
and the VALUES clause at once::
>>> from sqlalchemy import insert
may also be specified explicitly using the :meth:`_sql.Insert.returning`
method; in this case, the :class:`_engine.Result`
object that's returned when the statement is executed has rows which
-can be fetched. It is only supported for single-statement
-forms, and for some backends may only support single-row INSERT statements
-overall::
+can be fetched::
>>> insert_stmt = insert(address_table).returning(address_table.c.id, address_table.c.email_address)
>>> print(insert_stmt)
VALUES (:id, :user_id, :email_address)
RETURNING address.id, address.email_address
-
It can also be combined with :meth:`_sql.Insert.from_select`,
as in the example below that builds upon the example stated in
:ref:`tutorial_insert_from_select`::
SELECT user_account.id, user_account.name || :name_1 AS anon_1
FROM user_account RETURNING address.id, address.email_address
+.. tip::
+
+ The RETURNING feature is also supported by UPDATE and DELETE statements,
+ which will be introduced later in this tutorial.
+ The RETURNING feature is generally [1]_ only
+ supported for statement executions that use a single set of bound
+ parameters; that is, it wont work with the "executemany" form introduced
+ at :ref:`tutorial_multiple_parameters`. Additionally, some dialects
+ such as the Oracle dialect only allow RETURNING to return a single row
+ overall, meaning it won't work with "INSERT..FROM SELECT" nor will it
+ work with multiple row :class:`_sql.Update` or :class:`_sql.Delete`
+ forms.
+
+ .. [1] There is internal support for the
+ :mod:`_postgresql.psycopg2` dialect to INSERT many rows at once
+ and also support RETURNING, which is leveraged by the SQLAlchemy
+ ORM. However this feature has not been generalized to all dialects
+ and is not yet part of SQLAlchemy's regular API.
+
+
+
.. seealso::
:class:`_sql.Insert` - in the SQL Expression API documentation
(User(id=1, name='spongebob', fullname='Spongebob Squarepants'),)
{opensql}ROLLBACK{stop}
+.. topic:: select() from a Table vs. ORM class
+
+ While the SQL generated in these examples looks the same whether we invoke
+ ``select(user_table)`` or ``select(User)``, in the more general case
+ they do not necessarily render the same thing, as an ORM-mapped class
+ may be mapped to other kinds of "selectables" besides tables. The
+ ``select()`` that's against an ORM entity also indicates that ORM-mapped
+ instances should be returned in a result, which is not the case when
+ SELECTing from a :class:`_schema.Table` object.
+
The following sections will discuss the SELECT construct in more detail.
conjunction with
:class:`_schema.Column` and similar objects. For boolean expressions, most
Python operators such as ``==``, ``!=``, ``<``, ``>=`` etc. generate new
-SQL Expression objects, rather than plain boolean True/False values::
+SQL Expression objects, rather than plain boolean ``True``/``False`` values::
>>> print(user_table.c.name == 'squidward')
user_account.name = :name_1
{opensql}SELECT user_account.name, address.email_address
FROM user_account, address
-In order to JOIN these two tables together, two methods that are
-most straightforward are :meth:`_sql.Select.join_from`, which
-allows us to indicate the left and right side of the JOIN explicitly::
+In order to JOIN these two tables together, we typically use one of two methods
+on :class:`_sql.Select`. The first is the :meth:`_sql.Select.join_from`
+method, which allows us to indicate the left and right side of the JOIN
+explicitly::
>>> print(
... select(user_table.c.name, address_table.c.email_address).
FROM user_account JOIN address ON user_account.id = address.user_id
-the other is the :meth:`_sql.Select.join` method, which indicates only the
+The other is the the :meth:`_sql.Select.join` method, which indicates only the
right side of the JOIN, the left hand-side is inferred::
>>> print(
.. sidebar:: The ON Clause is inferred
When using :meth:`_sql.Select.join_from` or :meth:`_sql.Select.join`, we may
- observe that the ON clause of the join is also inferred for us in simple cases.
- More on that in the next section.
+ observe that the ON clause of the join is also inferred for us in simple
+ foreign key cases. More on that in the next section.
We also have the option add elements to the FROM clause explicitly, if it is not
inferred the way we want from the columns clause. We use the
Setting the ON Clause
~~~~~~~~~~~~~~~~~~~~~
-The previous examples on JOIN illustrated that the :class:`_sql.Select` construct
+The previous examples of JOIN illustrated that the :class:`_sql.Select` construct
can join between two tables and produce the ON clause automatically. This
occurs in those examples because the ``user_table`` and ``address_table``
:class:`_sql.Table` objects include a single :class:`_schema.ForeignKeyConstraint`
.. container:: orm-header
**ORM Tip** - there's another way to generate the ON clause when using
- ORM entities as well, when using the :func:`_orm.relationship` construct
- that can be seen in the mapping set up at :ref:`tutorial_declaring_mapped_classes`.
- This is a whole subject onto itself, which is introduced more fully
+ ORM entities that make use of the :func:`_orm.relationship` construct,
+ like the mapping set up in the previous section at
+ :ref:`tutorial_declaring_mapped_classes`.
+ This is a whole subject onto itself, which is introduced at length
at :ref:`tutorial_joining_relationships`.
OUTER and FULL join
... select(user_table).join(address_table, isouter=True)
... )
{opensql}SELECT user_account.id, user_account.name, user_account.fullname
- FROM user_account LEFT OUTER JOIN address ON user_account.id = address.user_id
+ FROM user_account LEFT OUTER JOIN address ON user_account.id = address.user_id{stop}
>>> print(
... select(user_table).join(address_table, full=True)
... )
{opensql}SELECT user_account.id, user_account.name, user_account.fullname
- FROM user_account FULL OUTER JOIN address ON user_account.id = address.user_id
+ FROM user_account FULL OUTER JOIN address ON user_account.id = address.user_id{stop}
There is also a method :meth:`_sql.Select.outerjoin` that is equivalent to
using ``.join(..., isouter=True)``.
+.. tip::
+
+ SQL also has a "RIGHT OUTER JOIN". SQLAlchemy doesn't render this directly;
+ instead, reverse the order of the tables and use "LEFT OUTER JOIN".
+
ORDER BY
^^^^^^^^^
SQLAlchemy provides for SQL functions in an open-ended way using a namespace
known as :data:`_sql.func`. This is a special constructor object which
will create new instances of :class:`_functions.Function` when given the name
-of a particular SQL function, which can be any name, as well as zero or
-more arguments to pass to the function, which are like in all other cases
+of a particular SQL function, which can have any name, as well as zero or
+more arguments to pass to the function, which are, like in all other cases,
SQL Expression constructs. For example, to
render the SQL COUNT() function against the ``user_account.id`` column,
we call upon the name ``count()`` name::
Ordering or Grouping by a Label
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-An important technique in particular on some database backends is the ability
+An important technique, in particular on some database backends, is the ability
to ORDER BY or GROUP BY an expression that is already stated in the columns
clause, without re-stating the expression in the ORDER BY or GROUP BY clause
and instead using the column name or labeled name from the COLUMNS clause.
RETURNING clause of an INSERT, UPDATE or DELETE statement. The docstring
for :class:`_sql.CTE` includes details on these additional patterns.
+In both cases, the subquery and CTE were named at the SQL level using an
+"anonymous" name. In the Python code, we don't need to provide these names
+at all. The object identity of the :class:`_sql.Subquery` or :class:`_sql.CTE`
+instances serves as the syntactical identity of the object when rendered.
+A name that will be rendered in the SQL can be provided by passing it as the
+first argument of the :meth:`_sql.Select.subquery` or :meth:`_sql.Select.cte` methods.
+
.. seealso::
:meth:`_sql.Select.subquery` - further detail on subqueries
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=3, email_address='sandy@squirrelpower.org')
{opensql}ROLLBACK{stop}
-In both cases, the subquery and CTE were named at the SQL level using an
-"anonymous" name. In the Python code, we don't need to provide these names
-at all. The object identity of the :class:`_sql.Subquery` or :class:`_sql.CTE`
-instances serves as the syntactical identity of the object when rendered.
-
.. _tutorial_scalar_subquery:
Scalar and Correlated Subqueries
{opensql}UPDATE some_table SET y=:y, x=(some_table.y + :y_1)
-.. [1] While Python dictionaries are `guaranteed to be insert ordered
+.. [1] While Python dictionaries are
+ `guaranteed to be insert ordered
<https://mail.python.org/pipermail/python-dev/2017-December/151283.html>`_
as of Python 3.7, the
:meth:`_sql.Update.ordered_values` method stilll provides an additional
The :func:`_sql.delete` statement from an API perspective is very similar to
that of the :func:`_sql.update` construct, traditionally returning no rows but
-allowing for a RETURNING variant.
+allowing for a RETURNING variant on some database backends.
::
>>> from sqlalchemy import delete
- >>> stmt = (
- ... delete(user_table).where(user_table.c.name == 'patrick')
- ... )
+ >>> stmt = delete(user_table).where(user_table.c.name == 'patrick')
>>> print(stmt)
{opensql}DELETE FROM user_account WHERE user_account.name = :name_1
>>> print(update_stmt)
{opensql}UPDATE user_account SET fullname=:fullname
WHERE user_account.name = :name_1
- RETURNING user_account.id, user_account.name
+ RETURNING user_account.id, user_account.name{stop}
>>> delete_stmt = (
... delete(user_table).where(user_table.c.name == 'patrick').
>>> print(delete_stmt)
{opensql}DELETE FROM user_account
WHERE user_account.name = :name_1
- RETURNING user_account.id, user_account.name
+ RETURNING user_account.id, user_account.name{stop}
Further Reading for UPDATE, DELETE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SQLAlchemy abstracts these formats into just one, which is the "named" format
using a colon.
+.. topic:: Always use bound parameters
+
+ As mentioned at the beginning of this section, textual SQL is not the usual
+ way we work with SQLAlchemy. However, when using textual SQL, a Python
+ literal value, even non-strings like integers or dates, should **never be
+ stringified into SQL string directly**; a parameter should **always** be
+ used. This is most famously known as how to avoid SQL injection attacks
+ when the data is untrusted. However it also allows the SQLAlchemy dialects
+ and/or DBAPI to correctly handle the incoming input for the backend.
+ Outside of plain textual SQL use cases, SQLAlchemy's Core Expression API
+ otherwise ensures that Python literal values are passed as bound parameters
+ where appropriate.
+
.. _tutorial_multiple_parameters:
Sending Multiple Parameters
driver that SQLAlchemy uses to interact with a particular database. In
this case, we're using the name ``pysqlite``, which in modern Python
use is the `sqlite3 <http://docs.python.org/library/sqlite3.html>`_ standard
- library interface for SQLite.
+ library interface for SQLite. If omitted, SQLAlchemy will use a default
+ :term:`DBAPI` for the particular database selected.
3. How do we locate the database? In this case, our URL includes the phrase
``/:memory:``, which is an indicator to the ``sqlite3`` module that we
within the 1.4 transitional phase should check out the
:ref:`migration_20_toplevel` document as well.
- For the newcomer, this document has a **lot** of detail, however at the
+ For the newcomer, this document has a **lot** of detail, however by the
end they will be considered an **Alchemist**.
SQLAlchemy is presented as two distinct APIs, one building on top of the other.
* :ref:`tutorial_working_with_data` - here we learn how to create, select,
update and delete data in the database. The so-called :term:`CRUD`
operations here are given in terms of SQLAlchemy Core with links out towards
- their ORM counterparts. The SELECT operation is deeply introduced at
+ their ORM counterparts. The SELECT operation that is introduced in detail at
:ref:`tutorial_selecting_data` applies equally well to Core and ORM.
* :ref:`tutorial_orm_data_manipulation` covers the persistence framework of the
of how it's used, with links to deeper documentation.
* :ref:`tutorial_further_reading` lists a series of major top-level
- documentation sections which fully document the concepts introduced in this
+ documentation sections which fully documents the concepts introduced in this
tutorial.
In a similar manner as in our Core examples of :class:`_sql.Insert`, we did not
include a primary key (i.e. an entry for the ``id`` column), since we would
-like to make use of SQLite's auto-incrementing primary key feature which the
-ORM also integrates with. The value of the ``id`` attribute on the above
+like to make use of the auto-incrementing primary key feature of the database,
+SQLite in this case, which the ORM also integrates with.
+The value of the ``id`` attribute on the above
objects, if we were to view it, displays itself as ``None``::
>>> squidward
INSERT INTO user_account (name, fullname) VALUES (?, ?)
[...] ('ehkrabs', 'Eugene H. Krabs')
-Above we observe the :class:`_orm.Session` was first called upon to emit
-SQL, so it created a new transaction and emitted the appropriate INSERT
-statements for the two objects. The transaction now **remains open**
-until we call the :meth:`_orm.Session.commit` method.
+Above we observe the :class:`_orm.Session` was first called upon to emit SQL,
+so it created a new transaction and emitted the appropriate INSERT statements
+for the two objects. The transaction now **remains open** until we call any
+of the :meth:`_orm.Session.commit`, :meth:`_orm.Session.rollback`, or
+:meth:`_orm.Session.close` methods of :class:`_orm.Session`.
While :meth:`_orm.Session.flush` may be used to manually push out pending
changes to the current transaction, it is usually unnecessary as the
way is that it is emitted automatically as part of the :term:`unit of work`
process used by the :class:`_orm.Session`, where an UPDATE statement is emitted
on a per-primary key basis corresponding to individual objects that have
-changes on them. A second form of ORM enabled UPDATE is called an "ORM enabled
+changes on them. A second form of UPDATE is called an "ORM enabled
UPDATE" and allows us to use the :class:`_sql.Update` construct with the
:class:`_orm.Session` explicitly; this is described in the next section.
Like UPDATE operations, there is also an ORM-enabled version of DELETE which we can
illustrate by using the :func:`_sql.delete` construct with
:meth:`_orm.Session.execute`. It also has a feature by which **non expired**
-objects that match the given deletion criteria will be automatically marked
-as "deleted" in the :class:`_orm.Session`:
+objects (see :term:`expired`) that match the given deletion criteria will be
+automatically marked as ":term:`deleted`" in the :class:`_orm.Session`:
.. sourcecode:: pycon+sql
>>> sandy.__dict__
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x...>}
-This is the "expired" state; accessing the attribute again will autobegin
+This is the ":term:`expired`" state; accessing the attribute again will autobegin
a new transaction and refresh ``sandy`` with the current database row:
.. sourcecode:: pycon+sql
.. _tutorial_orm_related_objects:
Working with Related Objects
-=============================
+============================
-In this section, we will cover one more essential ORM concept, which is that of
+In this section, we will cover one more essential ORM concept, which is
how the ORM interacts with mapped classes that refer to other objects. In the
section :ref:`tutorial_declaring_mapped_classes`, the mapped class examples
made use of a construct called :func:`_orm.relationship`. This construct
It's at this stage that we can see the very great utility that the unit of
work process provides; recall in the section :ref:`tutorial_core_insert_values_clause`,
-rows were inserted rows into the ``user_account`` and
+rows were inserted into the ``user_account`` and
``address`` tables using some elaborate syntaxes in order to automatically
associate the ``address.user_id`` columns with those of the ``user_account``
rows. Additionally, it was necessary that we emit INSERT for ``user_account``
.. _tutorial_loading_relationships:
Loading Relationships
-----------------------
+---------------------
In the last step, we called :meth:`_orm.Session.commit` which emitted a COMMIT
for the transaction, and then per
.. _tutorial_select_relationships:
Using Relationships in Queries
--------------------------------
+------------------------------
The previous section introduced the behavior of the :func:`_orm.relationship`
construct when working with **instances of a mapped class**, above, the
-``u1``, ``a1`` and ``a2`` instances of the ``User`` and ``Address`` class.
+``u1``, ``a1`` and ``a2`` instances of the ``User`` and ``Address`` classes.
In this section, we introduce the behavior of :func:`_orm.relationship` as it
applies to **class level behavior of a mapped class**, where it serves in
several ways to help automate the construction of SQL queries.
.. _tutorial_joining_relationships_aliased:
Joining between Aliased targets
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the section :ref:`tutorial_orm_entity_aliases` we introduced the
:func:`_orm.aliased` construct, which is used to apply a SQL alias to an
.. _tutorial_relationship_exists:
-EXISTS forms / has() / any()
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+EXISTS forms: has() / any()
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the section :ref:`tutorial_exists`, we introduced the :class:`_sql.Exists`
object that provides for the SQL EXISTS keyword in conjunction with a
.. _tutorial_relationship_operators:
Common Relationship Operators
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are some additional varieties of SQL generation helpers that come with
:func:`_orm.relationship`, including:
loader strategies
Selectin Load
-^^^^^^^^^^^^^^
+^^^^^^^^^^^^^
The most useful loader in modern SQLAlchemy is the
:func:`_orm.selectinload` loader option. This option solves the most common
pearl.krabs@gmail.com pkrabs
pearl@aol.com pkrabs
-:func:`_orm.joinedload` also works for collections, however it has the effect
+:func:`_orm.joinedload` also works for collections, meaning one-to-many relationships,
+however it has the effect
of multiplying out primary rows per related item in a recursive way
that grows the amount of data sent for a result set by orders of magnitude for
nested collections and/or larger collections, so its use vs. another option
include additional criteria in the ON clause. The :meth:`_orm.PropComparator.and_`
method is in fact generally available for most loader options. For example,
if we wanted to re-load the names of users and their email addresses, but omitting
-the email addresses at the ``sqlalchemy.org`` domain, we can apply
+the email addresses with the ``sqlalchemy.org`` domain, we can apply
:meth:`_orm.PropComparator.and_` to the argument passed to
:func:`_orm.selectinload` to limit this criteria:
One additional loader strategy worth mentioning is :func:`_orm.raiseload`.
This option is used to completely block an application from having the
:term:`N plus one` problem at all by causing what would normally be a lazy
-load to raise instead. It has two variants that are controlled via
+load to raise an error instead. It has two variants that are controlled via
the :paramref:`_orm.raiseload.sql_only` option to block either lazy loads
that require SQL, versus all "load" operations including those which
only need to consult the current :class:`_orm.Session`.
from . import exc # noqa
from . import mapper as mapperlib # noqa
from . import strategy_options
+from .attributes import AttributeEvent # noqa
+from .attributes import InstrumentedAttribute # noqa
+from .attributes import QueryableAttribute # noqa
+from .context import QueryContext # noqa
from .decl_api import as_declarative # noqa
from .decl_api import declarative_base # noqa
from .decl_api import declared_attr # noqa
from .decl_api import synonym_for # noqa
from .descriptor_props import CompositeProperty # noqa
from .descriptor_props import SynonymProperty # noqa
+from .identity import IdentityMap # noqa
+from .instrumentation import ClassManager # noqa
from .interfaces import EXT_CONTINUE # noqa
from .interfaces import EXT_SKIP # noqa
from .interfaces import EXT_STOP # noqa
+from .interfaces import InspectionAttr # noqa
+from .interfaces import InspectionAttrInfo # noqa
from .interfaces import MANYTOMANY # noqa
from .interfaces import MANYTOONE # noqa
from .interfaces import MapperProperty # noqa
+from .interfaces import NOT_EXTENSION # noqa
from .interfaces import ONETOMANY # noqa
from .interfaces import PropComparator # noqa
+from .loading import merge_frozen_result # noqa
+from .loading import merge_result # noqa
from .mapper import _mapper_registry
from .mapper import class_mapper # noqa
from .mapper import configure_mappers # noqa
from .session import Session # noqa
from .session import sessionmaker # noqa
from .session import SessionTransaction # noqa
+from .state import AttributeState # noqa
+from .state import InstanceState # noqa
from .strategy_options import Load # noqa
+from .unitofwork import UOWTransaction # noqa
from .util import aliased # noqa
from .util import Bundle # noqa
+from .util import CascadeOptions # noqa
from .util import join # noqa
from .util import LoaderCriteriaOption # noqa
from .util import object_mapper # noqa
OP_MODIFIED = util.symbol("MODIFIED")
-class Event(object):
+class AttributeEvent(object):
"""A token propagated throughout the course of a chain of attribute
events.
def __eq__(self, other):
return (
- isinstance(other, Event)
+ isinstance(other, AttributeEvent)
and other.impl is self.impl
and other.op == self.op
)
return self.impl.hasparent(state)
+Event = AttributeEvent
+
+
class AttributeImpl(object):
"""internal implementation for instrumented attributes."""
"""
- for criterion in list(whereclause):
+ for criterion in whereclause:
where_criteria = coercions.expect(roles.WhereHavingRole, criterion)
self._where_criteria += (where_criteria,)
assert isinstance(self._where_criteria, tuple)
- for criterion in list(whereclause):
+ for criterion in whereclause:
where_criteria = coercions.expect(roles.WhereHavingRole, criterion)
self._where_criteria += (where_criteria,)