Both FromCache and RelationCache are simpler individually.
- documentation
- - Major cleanup work in the docs to link class names into
- the API docs. [ticket:1700/1702]
+ - Major cleanup work in the docs to link class, function, and
+ method names into the API docs. [ticket:1700/1702/1703]
0.6beta1
========
Database Engine Options
========================
-Keyword options can also be specified to ``create_engine()``, following the string URL as follows:
+Keyword options can also be specified to :func:`~sqlalchemy.create_engine`, following the string URL as follows:
.. sourcecode:: python+sql
Configuring Logging
====================
-Python's standard `logging <http://www.python.org/doc/lib/module-logging.html>`_ module is used to implement informational and debug log output with SQLAlchemy. This allows SQLAlchemy's logging to integrate in a standard way with other applications and libraries. The ``echo`` and ``echo_pool`` flags that are present on ``create_engine()``, as well as the ``echo_uow`` flag used on :class:`~sqlalchemy.orm.session.Session`, all interact with regular loggers.
+Python's standard `logging <http://www.python.org/doc/lib/module-logging.html>`_ module is used to implement informational and debug log output with SQLAlchemy. This allows SQLAlchemy's logging to integrate in a standard way with other applications and libraries. The ``echo`` and ``echo_pool`` flags that are present on :func:`~sqlalchemy.create_engine`, as well as the ``echo_uow`` flag used on :class:`~sqlalchemy.orm.session.Session`, all interact with regular loggers.
This section assumes familiarity with the above linked logging module. All logging performed by SQLAlchemy exists underneath the ``sqlalchemy`` namespace, as used by ``logging.getLogger('sqlalchemy')``. When logging has been configured (i.e. such as via ``logging.basicConfig()``), the general namespace of SA loggers that can be turned on is as follows:
By default, the log level is set to ``logging.ERROR`` within the entire ``sqlalchemy`` namespace so that no log operations occur, even within an application that has logging enabled otherwise.
-The ``echo`` flags present as keyword arguments to ``create_engine()`` and others as well as the ``echo`` property on :class:`~sqlalchemy.engine.base.Engine`, when set to ``True``, will first attempt to ensure that logging is enabled. Unfortunately, the ``logging`` module provides no way of determining if output has already been configured (note we are referring to if a logging configuration has been set up, not just that the logging level is set). For this reason, any ``echo=True`` flags will result in a call to ``logging.basicConfig()`` using sys.stdout as the destination. It also sets up a default format using the level name, timestamp, and logger name. Note that this configuration has the affect of being configured **in addition** to any existing logger configurations. Therefore, **when using Python logging, ensure all echo flags are set to False at all times**, to avoid getting duplicate log lines.
+The ``echo`` flags present as keyword arguments to :func:`~sqlalchemy.create_engine` and others as well as the ``echo`` property on :class:`~sqlalchemy.engine.base.Engine`, when set to ``True``, will first attempt to ensure that logging is enabled. Unfortunately, the ``logging`` module provides no way of determining if output has already been configured (note we are referring to if a logging configuration has been set up, not just that the logging level is set). For this reason, any ``echo=True`` flags will result in a call to ``logging.basicConfig()`` using sys.stdout as the destination. It also sets up a default format using the level name, timestamp, and logger name. Note that this configuration has the affect of being configured **in addition** to any existing logger configurations. Therefore, **when using Python logging, ensure all echo flags are set to False at all times**, to avoid getting duplicate log lines.
'_email': addresses_table.c.email
})
-However, the approach above is not complete. While our ``EmailAddress`` object will shuttle the value through the ``email`` descriptor and into the ``_email`` mapped attribute, the class level ``EmailAddress.email`` attribute does not have the usual expression semantics usable with :class:`~sqlalchemy.orm.query.Query`. To provide these, we instead use the ``synonym()`` function as follows:
+However, the approach above is not complete. While our ``EmailAddress`` object will shuttle the value through the ``email`` descriptor and into the ``_email`` mapped attribute, the class level ``EmailAddress.email`` attribute does not have the usual expression semantics usable with :class:`~sqlalchemy.orm.query.Query`. To provide these, we instead use the :func:`~sqlalchemy.orm.synonym` function as follows:
.. sourcecode:: python+sql
q = session.query(EmailAddress).filter_by(email='some other address')
-If the mapped class does not provide a property, the ``synonym()`` construct will create a default getter/setter object automatically.
+If the mapped class does not provide a property, the :func:`~sqlalchemy.orm.synonym` construct will create a default getter/setter object automatically.
.. _custom_comparators:
There are four kinds of ``Comparator`` classes which may be subclassed, as according to the type of mapper property configured:
- * ``column_property()`` attribute - ``sqlalchemy.orm.properties.ColumnProperty.Comparator``
- * ``composite()`` attribute - ``sqlalchemy.orm.properties.CompositeProperty.Comparator``
- * ``relation()`` attribute - ``sqlalchemy.orm.properties.RelationProperty.Comparator``
- * ``comparable_property()`` attribute - ``sqlalchemy.orm.interfaces.PropComparator``
+ * :func:`~sqlalchemy.orm.column_property` attribute - ``sqlalchemy.orm.properties.ColumnProperty.Comparator``
+ * :func:`~sqlalchemy.orm.composite` attribute - ``sqlalchemy.orm.properties.CompositeProperty.Comparator``
+ * :func:`~sqlalchemy.orm.relation` attribute - ``sqlalchemy.orm.properties.RelationProperty.Comparator``
+ * :func:`~sqlalchemy.orm.comparable_property` attribute - ``sqlalchemy.orm.interfaces.PropComparator``
-When using ``comparable_property()``, which is a mapper property that isn't tied to any column or mapped table, the ``__clause_element__()`` method of :class:`~sqlalchemy.orm.interfaces.PropComparator` should also be implemented.
+When using :func:`~sqlalchemy.orm.comparable_property`, which is a mapper property that isn't tied to any column or mapped table, the ``__clause_element__()`` method of :class:`~sqlalchemy.orm.interfaces.PropComparator` should also be implemented.
-The ``comparator_factory`` argument is accepted by all ``MapperProperty``-producing functions: ``column_property()``, ``composite()``, ``comparable_property()``, ``synonym()``, ``relation()``, ``backref()``, ``deferred()``, and ``dynamic_loader()``.
+The ``comparator_factory`` argument is accepted by all ``MapperProperty``-producing functions: :func:`~sqlalchemy.orm.column_property`, :func:`~sqlalchemy.orm.composite`, :func:`~sqlalchemy.orm.comparable_property`, :func:`~sqlalchemy.orm.synonym`, :func:`~sqlalchemy.orm.relation`, ``backref()``, :func:`~sqlalchemy.orm.deferred`, and :func:`~sqlalchemy.orm.dynamic_loader`.
Composite Column Types
-----------------------
The ORM does not generate ordering for any query unless explicitly configured.
-The "default" ordering for a collection, which applies to list-based collections, can be configured using the ``order_by`` keyword argument on ``relation()``::
+The "default" ordering for a collection, which applies to list-based collections, can be configured using the ``order_by`` keyword argument on :func:`~sqlalchemy.orm.relation`::
mapper(Address, addresses_table)
'addresses': relation(Address, order_by=addresses_table.c.address_id)
})
-Note that when using eager loaders with relations, the tables used by the eager load's join are anonymously aliased. You can only order by these columns if you specify it at the ``relation()`` level. To control ordering at the query level based on a related table, you ``join()`` to that relation, then order by it::
+Note that when using eager loaders with relations, the tables used by the eager load's join are anonymously aliased. You can only order by these columns if you specify it at the :func:`~sqlalchemy.orm.relation` level. To control ordering at the query level based on a related table, you ``join()`` to that relation, then order by it::
session.query(User).join('addresses').order_by(Address.street)
Creating Joins to Specific Subtypes
++++++++++++++++++++++++++++++++++++
-The ``of_type()`` method is a helper which allows the construction of joins along ``relation`` paths while narrowing the criterion to specific subclasses. Suppose the ``employees`` table represents a collection of employees which are associated with a ``Company`` object. We'll add a ``company_id`` column to the ``employees`` table and a new table ``companies``:
+The :func:`~sqlalchemy.orm.interfaces.PropComparator.of_type` method is a helper which allows the construction of joins along ``relation`` paths while narrowing the criterion to specific subclasses. Suppose the ``employees`` table represents a collection of employees which are associated with a ``Company`` object. We'll add a ``company_id`` column to the ``employees`` table and a new table ``companies``:
.. sourcecode:: python+sql
'employees': relation(Employee)
})
-When querying from ``Company`` onto the ``Employee`` relation, the ``join()`` method as well as the ``any()`` and ``has()`` operators will create a join from ``companies`` to ``employees``, without including ``engineers`` or ``managers`` in the mix. If we wish to have criterion which is specifically against the ``Engineer`` class, we can tell those methods to join or subquery against the joined table representing the subclass using the ``of_type()`` operator:
+When querying from ``Company`` onto the ``Employee`` relation, the ``join()`` method as well as the ``any()`` and ``has()`` operators will create a join from ``companies`` to ``employees``, without including ``engineers`` or ``managers`` in the mix. If we wish to have criterion which is specifically against the ``Engineer`` class, we can tell those methods to join or subquery against the joined table representing the subclass using the :func:`~sqlalchemy.orm.interfaces.PropComparator.of_type` operator:
.. sourcecode:: python+sql
session.query(Company).join((employees.join(engineers), Company.employees)).filter(Engineer.engineer_info=='someinfo')
-Currently, ``of_type()`` accepts a single class argument. It may be expanded later on to accept multiple classes. For now, to join to any group of subclasses, the longhand notation allows this flexibility:
+Currently, :func:`~sqlalchemy.orm.interfaces.PropComparator.of_type` accepts a single class argument. It may be expanded later on to accept multiple classes. For now, to join to any group of subclasses, the longhand notation allows this flexibility:
.. sourcecode:: python+sql
session.query(Company).join((employees.outerjoin(engineers).outerjoin(managers), Company.employees)).\
filter(or_(Engineer.engineer_info=='someinfo', Manager.manager_data=='somedata'))
-The ``any()`` and ``has()`` operators also can be used with ``of_type()`` when the embedded criterion is in terms of a subclass:
+The ``any()`` and ``has()`` operators also can be used with :func:`~sqlalchemy.orm.interfaces.PropComparator.of_type` when the embedded criterion is in terms of a subclass:
.. sourcecode:: python+sql
Multiple Mappers for One Class
-------------------------------
-The first mapper created for a certain class is known as that class's "primary mapper." Other mappers can be created as well on the "load side" - these are called **secondary mappers**. This is a mapper that must be constructed with the keyword argument ``non_primary=True``, and represents a load-only mapper. Objects that are loaded with a secondary mapper will have their save operation processed by the primary mapper. It is also invalid to add new ``relation()`` objects to a non-primary mapper. To use this mapper with the Session, specify it to the ``query`` method:
+The first mapper created for a certain class is known as that class's "primary mapper." Other mappers can be created as well on the "load side" - these are called **secondary mappers**. This is a mapper that must be constructed with the keyword argument ``non_primary=True``, and represents a load-only mapper. Objects that are loaded with a secondary mapper will have their save operation processed by the primary mapper. It is also invalid to add new :func:`~sqlalchemy.orm.relation` objects to a non-primary mapper. To use this mapper with the Session, specify it to the :class:`~sqlalchemy.orm.session.Session.query` method:
example:
# select
result = session.query(othermapper).select()
-The "non primary mapper" is a rarely needed feature of SQLAlchemy; in most cases, the :class:`~sqlalchemy.orm.query.Query` object can produce any kind of query that's desired. It's recommended that a straight :class:`~sqlalchemy.orm.query.Query` be used in place of a non-primary mapper unless the mapper approach is absolutely needed. Current use cases for the "non primary mapper" are when you want to map the class to a particular select statement or view to which additional query criterion can be added, and for when the particular mapped select statement or view is to be placed in a ``relation()`` of a parent mapper.
+The "non primary mapper" is a rarely needed feature of SQLAlchemy; in most cases, the :class:`~sqlalchemy.orm.query.Query` object can produce any kind of query that's desired. It's recommended that a straight :class:`~sqlalchemy.orm.query.Query` be used in place of a non-primary mapper unless the mapper approach is absolutely needed. Current use cases for the "non primary mapper" are when you want to map the class to a particular select statement or view to which additional query criterion can be added, and for when the particular mapped select statement or view is to be placed in a :func:`~sqlalchemy.orm.relation` of a parent mapper.
Multiple "Persistence" Mappers for One Class
---------------------------------------------
~~~~~~~~~~~~~
-Many to Many adds an association table between two classes. The association table is indicated by the ``secondary`` argument to ``relation()``.
+Many to Many adds an association table between two classes. The association table is indicated by the ``secondary`` argument to :func:`~sqlalchemy.orm.relation`.
.. sourcecode:: python+sql
To enhance the association object pattern such that direct access to the ``Association`` object is optional, SQLAlchemy provides the :ref:`associationproxy`.
-**Important Note**: it is strongly advised that the ``secondary`` table argument not be combined with the Association Object pattern, unless the ``relation()`` which contains the ``secondary`` argument is marked ``viewonly=True``. Otherwise, SQLAlchemy may persist conflicting data to the underlying association table since it is represented by two conflicting mappings. The Association Proxy pattern should be favored in the case where access to the underlying association data is only sometimes needed.
+**Important Note**: it is strongly advised that the ``secondary`` table argument not be combined with the Association Object pattern, unless the :func:`~sqlalchemy.orm.relation` which contains the ``secondary`` argument is marked ``viewonly=True``. Otherwise, SQLAlchemy may persist conflicting data to the underlying association table since it is represented by two conflicting mappings. The Association Proxy pattern should be favored in the case where access to the underlying association data is only sometimes needed.
Adjacency List Relationships
-----------------------------
Specifying Alternate Join Conditions to relation()
---------------------------------------------------
-The ``relation()`` function uses the foreign key relationship between the parent and child tables to formulate the **primary join condition** between parent and child; in the case of a many-to-many relationship it also formulates the **secondary join condition**::
+The :func:`~sqlalchemy.orm.relation` function uses the foreign key relationship between the parent and child tables to formulate the **primary join condition** between parent and child; in the case of a many-to-many relationship it also formulates the **secondary join condition**::
one to many/many to one:
------------------------
~~~~~~~~~~~~~~~~~~~~~~~~
-When using ``primaryjoin`` and ``secondaryjoin``, SQLAlchemy also needs to be aware of which columns in the relation reference the other. In most cases, a :class:`~sqlalchemy.schema.Table` construct will have :class:`~sqlalchemy.schema.ForeignKey` constructs which take care of this; however, in the case of reflected tables on a database that does not report FKs (like MySQL ISAM) or when using join conditions on columns that don't have foreign keys, the ``relation()`` needs to be told specifically which columns are "foreign" using the ``foreign_keys`` collection:
+When using ``primaryjoin`` and ``secondaryjoin``, SQLAlchemy also needs to be aware of which columns in the relation reference the other. In most cases, a :class:`~sqlalchemy.schema.Table` construct will have :class:`~sqlalchemy.schema.ForeignKey` constructs which take care of this; however, in the case of reflected tables on a database that does not report FKs (like MySQL ISAM) or when using join conditions on columns that don't have foreign keys, the :func:`~sqlalchemy.orm.relation` needs to be told specifically which columns are "foreign" using the ``foreign_keys`` collection:
.. sourcecode:: python+sql
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Very ambitious custom join conditions may fail to be directly persistable, and in some cases may not even load correctly. To remove the persistence part of the equation, use the flag ``viewonly=True`` on the ``relation()``, which establishes it as a read-only attribute (data written to the collection will be ignored on flush()). However, in extreme cases, consider using a regular Python property in conjunction with :class:`~sqlalchemy.orm.query.Query` as follows:
+Very ambitious custom join conditions may fail to be directly persistable, and in some cases may not even load correctly. To remove the persistence part of the equation, use the flag ``viewonly=True`` on the :func:`~sqlalchemy.orm.relation`, which establishes it as a read-only attribute (data written to the collection will be ignored on flush()). However, in extreme cases, consider using a regular Python property in conjunction with :class:`~sqlalchemy.orm.query.Query` as follows:
.. sourcecode:: python+sql
widget_id name favorite_entry_id entry_id name widget_id
1 'somewidget' 5 5 'someentry' 1
-In the first case, a row points to itself. Technically, a database that uses sequences such as PostgreSQL or Oracle can INSERT the row at once using a previously generated value, but databases which rely upon autoincrement-style primary key identifiers cannot. The ``relation()`` always assumes a "parent/child" model of row population during flush, so unless you are populating the primary key/foreign key columns directly, ``relation()`` needs to use two statements.
+In the first case, a row points to itself. Technically, a database that uses sequences such as PostgreSQL or Oracle can INSERT the row at once using a previously generated value, but databases which rely upon autoincrement-style primary key identifiers cannot. The :func:`~sqlalchemy.orm.relation` always assumes a "parent/child" model of row population during flush, so unless you are populating the primary key/foreign key columns directly, :func:`~sqlalchemy.orm.relation` needs to use two statements.
-In the second case, the "widget" row must be inserted before any referring "entry" rows, but then the "favorite_entry_id" column of that "widget" row cannot be set until the "entry" rows have been generated. In this case, it's typically impossible to insert the "widget" and "entry" rows using just two INSERT statements; an UPDATE must be performed in order to keep foreign key constraints fulfilled. The exception is if the foreign keys are configured as "deferred until commit" (a feature some databases support) and if the identifiers were populated manually (again essentially bypassing ``relation()``).
+In the second case, the "widget" row must be inserted before any referring "entry" rows, but then the "favorite_entry_id" column of that "widget" row cannot be set until the "entry" rows have been generated. In this case, it's typically impossible to insert the "widget" and "entry" rows using just two INSERT statements; an UPDATE must be performed in order to keep foreign key constraints fulfilled. The exception is if the foreign keys are configured as "deferred until commit" (a feature some databases support) and if the identifiers were populated manually (again essentially bypassing :func:`~sqlalchemy.orm.relation`).
-To enable the UPDATE after INSERT / UPDATE before DELETE behavior on ``relation()``, use the ``post_update`` flag on *one* of the relations, preferably the many-to-one side::
+To enable the UPDATE after INSERT / UPDATE before DELETE behavior on :func:`~sqlalchemy.orm.relation`, use the ``post_update`` flag on *one* of the relations, preferably the many-to-one side::
mapper(Widget, widget, properties={
'entries':relation(Entry, primaryjoin=widget.c.widget_id==entry.c.widget_id),
parent.children.append(Child())
print parent.children[0]
-Collections are not limited to lists. Sets, mutable sequences and almost any other Python object that can act as a container can be used in place of the default list, by specifying the ``collection_class`` option on ``relation()``.
+Collections are not limited to lists. Sets, mutable sequences and almost any other Python object that can act as a container can be used in place of the default list, by specifying the ``collection_class`` option on :func:`~sqlalchemy.orm.relation`.
.. sourcecode:: python+sql
WHERE users.name = ?
['jack']
-By default, all inter-object relationships are **lazy loading**. The scalar or collection attribute associated with a ``relation()`` contains a trigger which fires the first time the attribute is accessed, which issues a SQL call at that point:
+By default, all inter-object relationships are **lazy loading**. The scalar or collection attribute associated with a :func:`~sqlalchemy.orm.relation` contains a trigger which fires the first time the attribute is accessed, which issues a SQL call at that point:
.. sourcecode:: python+sql
[5]
{stop}[<Address(u'jack@google.com')>, <Address(u'j25@yahoo.com')>]
-The default **loader strategy** for any ``relation()`` is configured by the ``lazy`` keyword argument, which defaults to ``True``. Below we set it as ``False`` so that the ``children`` relation is eager loading:
+The default **loader strategy** for any :func:`~sqlalchemy.orm.relation` is configured by the ``lazy`` keyword argument, which defaults to ``True``. Below we set it as ``False`` so that the ``children`` relation is eager loading:
.. sourcecode:: python+sql
Working with Large Collections
-------------------------------
-The default behavior of ``relation()`` is to fully load the collection of items in, as according to the loading strategy of the relation. Additionally, the Session by default only knows how to delete objects which are actually present within the session. When a parent instance is marked for deletion and flushed, the Session loads its full list of child items in so that they may either be deleted as well, or have their foreign key value set to null; this is to avoid constraint violations. For large collections of child items, there are several strategies to bypass full loading of child items both at load time as well as deletion time.
+The default behavior of :func:`~sqlalchemy.orm.relation` is to fully load the collection of items in, as according to the loading strategy of the relation. Additionally, the Session by default only knows how to delete objects which are actually present within the session. When a parent instance is marked for deletion and flushed, the Session loads its full list of child items in so that they may either be deleted as well, or have their foreign key value set to null; this is to avoid constraint violations. For large collections of child items, there are several strategies to bypass full loading of child items both at load time as well as deletion time.
Dynamic Relation Loaders
~~~~~~~~~~~~~~~~~~~~~~~~~
-The most useful by far is the ``dynamic_loader()`` relation. This is a variant of ``relation()`` which returns a :class:`~sqlalchemy.orm.query.Query` object in place of a collection when accessed. ``filter()`` criterion may be applied as well as limits and offsets, either explicitly or via array slices:
+The most useful by far is the :func:`~sqlalchemy.orm.dynamic_loader` relation. This is a variant of :func:`~sqlalchemy.orm.relation` which returns a :class:`~sqlalchemy.orm.query.Query` object in place of a collection when accessed. ``filter()`` criterion may be applied as well as limits and offsets, either explicitly or via array slices:
.. sourcecode:: python+sql
Above, a table called ``user`` is described, which contains four columns. The primary key of the table consists of the ``user_id`` column. Multiple columns may be assigned the ``primary_key=True`` flag which denotes a multi-column primary key, known as a *composite* primary key.
-Note also that each column describes its datatype using objects corresponding to genericized types, such as ``Integer`` and ``String``. SQLAlchemy features dozens of types of varying levels of specificity as well as the ability to create custom types. Documentation on the type system can be found at :ref:`types`.
+Note also that each column describes its datatype using objects corresponding to genericized types, such as :class:`~sqlalchemy.types.Integer` and :class:`~sqlalchemy.types.String`. SQLAlchemy features dozens of types of varying levels of specificity as well as the ability to create custom types. Documentation on the type system can be found at :ref:`types`.
Accessing Tables and Columns
----------------------------
Once you've defined some :class:`~sqlalchemy.schema.Table` objects, assuming you're working with a brand new database one thing you might want to do is issue CREATE statements for those tables and their related constructs (as an aside, it's also quite possible that you *don't* want to do this, if you already have some preferred methodology such as tools included with your database or an existing scripting system - if that's the case, feel free to skip this section - SQLAlchemy has no requirement that it be used to create your tables).
-The usual way to issue CREATE is to use ``create_all()`` on the :class:`~sqlalchemy.schema.MetaData` object. This method will issue queries that first check for the existence of each individual table, and if not found will issue the CREATE statements:
+The usual way to issue CREATE is to use :func:`~sqlalchemy.schema.MetaData.create_all` on the :class:`~sqlalchemy.schema.MetaData` object. This method will issue queries that first check for the existence of each individual table, and if not found will issue the CREATE statements:
.. sourcecode:: python+sql
pref_value VARCHAR(100)
)
-``create_all()`` creates foreign key constraints between tables usually inline with the table definition itself, and for this reason it also generates the tables in order of their dependency. There are options to change this behavior such that ``ALTER TABLE`` is used instead.
+:func:`~sqlalchemy.schema.MetaData.create_all` creates foreign key constraints between tables usually inline with the table definition itself, and for this reason it also generates the tables in order of their dependency. There are options to change this behavior such that ``ALTER TABLE`` is used instead.
-Dropping all tables is similarly achieved using the ``drop_all()`` method. This method does the exact opposite of ``create_all()`` - the presence of each table is checked first, and tables are dropped in reverse order of dependency.
+Dropping all tables is similarly achieved using the :func:`~sqlalchemy.schema.MetaData.drop_all` method. This method does the exact opposite of :func:`~sqlalchemy.schema.MetaData.create_all` - the presence of each table is checked first, and tables are dropped in reverse order of dependency.
Creating and dropping individual tables can be done via the ``create()`` and ``drop()`` methods of :class:`~sqlalchemy.schema.Table`. These methods by default issue the CREATE or DROP regardless of the table being present:
# bind to an engine
meta.bind = engine
-We can now call methods like ``create_all()`` without needing to pass the :class:`~sqlalchemy.engine.base.Engine`::
+We can now call methods like :func:`~sqlalchemy.schema.MetaData.create_all` without needing to pass the :class:`~sqlalchemy.engine.base.Engine`::
meta.create_all()
Reflecting All Tables at Once
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The :class:`~sqlalchemy.schema.MetaData` object can also get a listing of tables and reflect the full set. This is achieved by using the ``reflect()`` method. After calling it, all located tables are present within the :class:`~sqlalchemy.schema.MetaData` object's dictionary of tables::
+The :class:`~sqlalchemy.schema.MetaData` object can also get a listing of tables and reflect the full set. This is achieved by using the :func:`~sqlalchemy.schema.MetaData.reflect` method. After calling it, all located tables are present within the :class:`~sqlalchemy.schema.MetaData` object's dictionary of tables::
meta = MetaData()
meta.reflect(bind=someengine)
* the statement is a single execution, i.e. only supplies one set of parameters and doesn't use "executemany" behavior
-* the ``inline=True`` flag is not set on the ``Insert()`` or ``Update()`` construct, and the statement has not defined an explicit `returning()` clause.
+* the ``inline=True`` flag is not set on the :class:`~sqlalchemy.sql.expression.Insert()` or :class:`~sqlalchemy.sql.expression.Update()` construct, and the statement has not defined an explicit `returning()` clause.
Whether or not the default generation clause "pre-executes" is not something that normally needs to be considered, unless it is being addressed for performance reasons.
Creating/Dropping Foreign Key Constraints via ALTER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In all the above examples, the :class:`~sqlalchemy.schema.ForeignKey` object causes the "REFERENCES" keyword to be added inline to a column definition within a "CREATE TABLE" statement when ``create_all()`` is issued, and :class:`~sqlalchemy.schema.ForeignKeyConstraint` invokes the "CONSTRAINT" keyword inline with "CREATE TABLE". There are some cases where this is undesireable, particularly when two tables reference each other mutually, each with a foreign key referencing the other. In such a situation at least one of the foreign key constraints must be generated after both tables have been built. To support such a scheme, :class:`~sqlalchemy.schema.ForeignKey` and :class:`~sqlalchemy.schema.ForeignKeyConstraint` offer the flag ``use_alter=True``. When using this flag, the constraint will be generated using a definition similar to "ALTER TABLE <tablename> ADD CONSTRAINT <name> ...". Since a name is required, the ``name`` attribute must also be specified. For example::
+In all the above examples, the :class:`~sqlalchemy.schema.ForeignKey` object causes the "REFERENCES" keyword to be added inline to a column definition within a "CREATE TABLE" statement when :func:`~sqlalchemy.schema.MetaData.create_all` is issued, and :class:`~sqlalchemy.schema.ForeignKeyConstraint` invokes the "CONSTRAINT" keyword inline with "CREATE TABLE". There are some cases where this is undesireable, particularly when two tables reference each other mutually, each with a foreign key referencing the other. In such a situation at least one of the foreign key constraints must be generated after both tables have been built. To support such a scheme, :class:`~sqlalchemy.schema.ForeignKey` and :class:`~sqlalchemy.schema.ForeignKeyConstraint` offer the flag ``use_alter=True``. When using this flag, the constraint will be generated using a definition similar to "ALTER TABLE <tablename> ADD CONSTRAINT <name> ...". Since a name is required, the ``name`` attribute must also be specified. For example::
node = Table('node', meta,
Column('node_id', Integer, primary_key=True),
Customizing DDL
===============
-In the preceding sections we've discussed a variety of schema constructs including :class:`~sqlalchemy.schema.Table`, :class:`~sqlalchemy.schema.ForeignKeyConstraint`, :class:`~sqlalchemy.schema.CheckConstraint`, and :class:`~sqlalchemy.schema.Sequence`. Throughout, we've relied upon the ``create()`` and ``create_all()`` methods of :class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.MetaData` in order to issue data definition language (DDL) for all constructs. When issued, a pre-determined order of operations is invoked, and DDL to create each table is created unconditionally including all constraints and other objects associated with it. For more complex scenarios where database-specific DDL is required, SQLAlchemy offers two techniques which can be used to add any DDL based on any condition, either accompanying the standard generation of tables or by itself.
+In the preceding sections we've discussed a variety of schema constructs including :class:`~sqlalchemy.schema.Table`, :class:`~sqlalchemy.schema.ForeignKeyConstraint`, :class:`~sqlalchemy.schema.CheckConstraint`, and :class:`~sqlalchemy.schema.Sequence`. Throughout, we've relied upon the ``create()`` and :func:`~sqlalchemy.schema.MetaData.create_all` methods of :class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.MetaData` in order to issue data definition language (DDL) for all constructs. When issued, a pre-determined order of operations is invoked, and DDL to create each table is created unconditionally including all constraints and other objects associated with it. For more complex scenarios where database-specific DDL is required, SQLAlchemy offers two techniques which can be used to add any DDL based on any condition, either accompanying the standard generation of tables or by itself.
Controlling DDL Sequences
-------------------------
col6 INTEGER
){stop}
-Above, the ``CreateTable`` construct works like any other expression construct (such as ``select()``, ``table.insert()``, etc.). A full reference of available constructs is in :ref:`schema_api_ddl`.
+Above, the :class:`~sqlalchemy.schema.CreateTable` construct works like any other expression construct (such as ``select()``, ``table.insert()``, etc.). A full reference of available constructs is in :ref:`schema_api_ddl`.
The DDL constructs all extend a common base class which provides the capability to be associated with an individual :class:`~sqlalchemy.schema.Table` or :class:`~sqlalchemy.schema.MetaData` object, to be invoked upon create/drop events. Consider the example of a table which contains a CHECK constraint:
Connecting
==========
-For this tutorial we will use an in-memory-only SQLite database. To connect we use ``create_engine()``::
+For this tutorial we will use an in-memory-only SQLite database. To connect we use :func:`~sqlalchemy.create_engine`::
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:', echo=True)
:ref:`metadata_toplevel` covers all about how to define :class:`~sqlalchemy.schema.Table` objects, as well as how to load their definition from an existing database (known as **reflection**).
-Next, we can issue CREATE TABLE statements derived from our table metadata, by calling ``create_all()`` and passing it the ``engine`` instance which points to our database. This will check for the presence of a table first before creating, so it's safe to call multiple times:
+Next, we can issue CREATE TABLE statements derived from our table metadata, by calling :func:`~sqlalchemy.schema.MetaData.create_all` and passing it the ``engine`` instance which points to our database. This will check for the presence of a table first before creating, so it's safe to call multiple times:
.. sourcecode:: python+sql
VARCHAR columns were generated without a length; on SQLite and Postgresql,
this is a valid datatype, but on others, it's not allowed. So if running
this tutorial on one of those databases, and you wish to use SQLAlchemy to
- issue CREATE TABLE, a "length" may be provided to the ``String`` type as
+ issue CREATE TABLE, a "length" may be provided to the :class:`~sqlalchemy.types.String` type as
below::
Column('name', String(50))
- The length field on ``String``, as well as similar precision/scale fields
- available on ``Integer``, ``Numeric``, etc. are not referenced by
+ The length field on :class:`~sqlalchemy.types.String`, as well as similar precision/scale fields
+ available on :class:`~sqlalchemy.types.Integer`, :class:`~sqlalchemy.types.Numeric`, etc. are not referenced by
SQLAlchemy other than when creating tables.
Additionally, Firebird and Oracle require sequences to generate new
... def __repr__(self):
... return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
-Above, the ``declarative_base()`` function defines a new class which
+Above, the :func:`~sqlalchemy.ext.declarative.declarative_base` function defines a new class which
we name ``Base``, from which all of our ORM-enabled classes will
derive. Note that we define :class:`~sqlalchemy.schema.Column`
objects with no "name" field, since it's inferred from the given
attribute name.
The underlying :class:`~sqlalchemy.schema.Table` object created by our
-``declarative_base()`` version of ``User`` is accessible via the
+:func:`~sqlalchemy.ext.declarative.declarative_base` version of ``User`` is accessible via the
``__table__`` attribute::
>>> users_table = User.__table__
Creating a Session
==================
-We're now ready to start talking to the database. The ORM's "handle" to the database is the :class:`~sqlalchemy.orm.session.Session`. When we first set up the application, at the same level as our ``create_engine()`` statement, we define a :class:`~sqlalchemy.orm.session.Session` class which will serve as a factory for new :class:`~sqlalchemy.orm.session.Session` objects:
+We're now ready to start talking to the database. The ORM's "handle" to the database is the :class:`~sqlalchemy.orm.session.Session`. When we first set up the application, at the same level as our :func:`~sqlalchemy.create_engine` statement, we define a :class:`~sqlalchemy.orm.session.Session` class which will serve as a factory for new :class:`~sqlalchemy.orm.session.Session` objects:
.. sourcecode:: python+sql
>>> Session = sessionmaker()
-Later, when you create your engine with ``create_engine()``, connect it to the :class:`~sqlalchemy.orm.session.Session` using ``configure()``:
+Later, when you create your engine with :func:`~sqlalchemy.create_engine`, connect it to the :class:`~sqlalchemy.orm.session.Session` using ``configure()``:
.. sourcecode:: python+sql
>>> Session.configure(bind=engine) # once engine is available
-This custom-made :class:`~sqlalchemy.orm.session.Session` class will create new :class:`~sqlalchemy.orm.session.Session` objects which are bound to our database. Other transactional characteristics may be defined when calling ``sessionmaker()`` as well; these are described in a later chapter. Then, whenever you need to have a conversation with the database, you instantiate a :class:`~sqlalchemy.orm.session.Session`::
+This custom-made :class:`~sqlalchemy.orm.session.Session` class will create new :class:`~sqlalchemy.orm.session.Session` objects which are bound to our database. Other transactional characteristics may be defined when calling :func:`~sqlalchemy.orm.sessionmaker` as well; these are described in a later chapter. Then, whenever you need to have a conversation with the database, you instantiate a :class:`~sqlalchemy.orm.session.Session`::
>>> session = Session()
The ORM concept at work here is known as an **identity map** and ensures that all operations upon a particular row within a :class:`~sqlalchemy.orm.session.Session` operate upon the same set of data. Once an object with a particular primary key is present in the :class:`~sqlalchemy.orm.session.Session`, all SQL queries on that :class:`~sqlalchemy.orm.session.Session` will always return the same Python object for that particular primary key; it also will raise an error if an attempt is made to place a second, already-persisted object with the same primary key within the session.
-We can add more ``User`` objects at once using ``add_all()``:
+We can add more ``User`` objects at once using :func:`~sqlalchemy.orm.session.Session.add_all`:
.. sourcecode:: python+sql
Querying
========
-A :class:`~sqlalchemy.orm.query.Query` is created using the ``query()`` function on :class:`~sqlalchemy.orm.session.Session`. This function takes a variable number of arguments, which can be any combination of classes and class-instrumented descriptors. Below, we indicate a :class:`~sqlalchemy.orm.query.Query` which loads ``User`` instances. When evaluated in an iterative context, the list of ``User`` objects present is returned:
+A :class:`~sqlalchemy.orm.query.Query` is created using the :class:`~sqlalchemy.orm.session.Session.query()` function on :class:`~sqlalchemy.orm.session.Session`. This function takes a variable number of arguments, which can be any combination of classes and class-instrumented descriptors. Below, we indicate a :class:`~sqlalchemy.orm.query.Query` which loads ``User`` instances. When evaluated in an iterative context, the list of ``User`` objects present is returned:
.. sourcecode:: python+sql
mary Mary Contrary
fred Fred Flinstone
-The :class:`~sqlalchemy.orm.query.Query` also accepts ORM-instrumented descriptors as arguments. Any time multiple class entities or column-based entities are expressed as arguments to the ``query()`` function, the return result is expressed as tuples:
+The :class:`~sqlalchemy.orm.query.Query` also accepts ORM-instrumented descriptors as arguments. Any time multiple class entities or column-based entities are expressed as arguments to the :class:`~sqlalchemy.orm.session.Session.query()` function, the return result is expressed as tuples:
.. sourcecode:: python+sql
<User('wendy','Wendy Williams', 'foobar')>
<User('mary','Mary Contrary', 'xxg527')>
-and filtering results, which is accomplished either with ``filter_by()``, which uses keyword arguments:
+and filtering results, which is accomplished either with :func:`~sqlalchemy.orm.query.Query.filter_by`, which uses keyword arguments:
.. sourcecode:: python+sql
The many-to-many relation is also bi-directional using the ``backref`` keyword. This is the one case where usage of ``backref`` is generally required, since if a separate ``posts`` relation were added to the ``Keyword`` entity, both relations would independently add and remove rows from the ``post_keywords`` table and produce conflicts.
-We would also like our ``BlogPost`` class to have an ``author`` field. We will add this as another bidirectional relationship, except one issue we'll have is that a single user might have lots of blog posts. When we access ``User.posts``, we'd like to be able to filter results further so as not to load the entire collection. For this we use a setting accepted by ``relation()`` called ``lazy='dynamic'``, which configures an alternate **loader strategy** on the attribute. To use it on the "reverse" side of a ``relation()``, we use the ``backref()`` function:
+We would also like our ``BlogPost`` class to have an ``author`` field. We will add this as another bidirectional relationship, except one issue we'll have is that a single user might have lots of blog posts. When we access ``User.posts``, we'd like to be able to filter results further so as not to load the entire collection. For this we use a setting accepted by :func:`~sqlalchemy.orm.relation` called ``lazy='dynamic'``, which configures an alternate **loader strategy** on the attribute. To use it on the "reverse" side of a :func:`~sqlalchemy.orm.relation`, we use the ``backref()`` function:
.. sourcecode:: python+sql
Getting a Session
=================
-:class:`~sqlalchemy.orm.session.Session` is a regular Python class which can be directly instantiated. However, to standardize how sessions are configured and acquired, the ``sessionmaker()`` function is normally used to create a top level :class:`~sqlalchemy.orm.session.Session` configuration which can then be used throughout an application without the need to repeat the configurational arguments.
+:class:`~sqlalchemy.orm.session.Session` is a regular Python class which can be directly instantiated. However, to standardize how sessions are configured and acquired, the :func:`~sqlalchemy.orm.sessionmaker` function is normally used to create a top level :class:`~sqlalchemy.orm.session.Session` configuration which can then be used throughout an application without the need to repeat the configurational arguments.
Using a sessionmaker() Configuration
------------------------------------
-The usage of ``sessionmaker()`` is illustrated below:
+The usage of :func:`~sqlalchemy.orm.sessionmaker` is illustrated below:
.. sourcecode:: python+sql
Above, the ``sessionmaker`` call creates a class for us, which we assign to the name :class:`~sqlalchemy.orm.session.Session`. This class is a subclass of the actual ``sqlalchemy.orm.session.Session`` class, which will instantiate with a particular bound engine.
-When you write your application, place the call to ``sessionmaker()`` somewhere global, and then make your new :class:`~sqlalchemy.orm.session.Session` class available to the rest of your application.
+When you write your application, place the call to :func:`~sqlalchemy.orm.sessionmaker` somewhere global, and then make your new :class:`~sqlalchemy.orm.session.Session` class available to the rest of your application.
Binding Session to an Engine
----------------------------
-In our previous example regarding ``sessionmaker()``, we specified a ``bind`` for a particular :class:`~sqlalchemy.engine.base.Engine`. If we'd like to construct a ``sessionmaker()`` without an engine available and bind it later on, or to specify other options to an existing ``sessionmaker()``, we may use the ``configure()`` method::
+In our previous example regarding :func:`~sqlalchemy.orm.sessionmaker`, we specified a ``bind`` for a particular :class:`~sqlalchemy.engine.base.Engine`. If we'd like to construct a :func:`~sqlalchemy.orm.sessionmaker` without an engine available and bind it later on, or to specify other options to an existing :func:`~sqlalchemy.orm.sessionmaker`, we may use the ``configure()`` method::
# configure Session class with desired options
Session = sessionmaker()
Using create_session()
----------------------
-As an alternative to ``sessionmaker()``, ``create_session()`` is a function which calls the normal :class:`~sqlalchemy.orm.session.Session` constructor directly. All arguments are passed through and the new :class:`~sqlalchemy.orm.session.Session` object is returned::
+As an alternative to :func:`~sqlalchemy.orm.sessionmaker`, :func:`~sqlalchemy.orm.create_session` is a function which calls the normal :class:`~sqlalchemy.orm.session.Session` constructor directly. All arguments are passed through and the new :class:`~sqlalchemy.orm.session.Session` object is returned::
session = create_session(bind=myengine, autocommit=True, autoflush=False)
-Note that ``create_session()`` disables all optional "automation" by default. Called with no arguments, the session produced is not autoflushing, does not auto-expire, and does not maintain a transaction (i.e. it begins and commits a new transaction for each ``flush()``). SQLAlchemy uses ``create_session()`` extensively within its own unit tests.
+Note that :func:`~sqlalchemy.orm.create_session` disables all optional "automation" by default. Called with no arguments, the session produced is not autoflushing, does not auto-expire, and does not maintain a transaction (i.e. it begins and commits a new transaction for each :func:`~sqlalchemy.orm.session.Session.flush`). SQLAlchemy uses :func:`~sqlalchemy.orm.create_session` extensively within its own unit tests.
Configurational Arguments
-------------------------
-Configurational arguments accepted by ``sessionmaker()`` and ``create_session()`` are the same as that of the :class:`~sqlalchemy.orm.session.Session` class itself, and are described at :func:`sqlalchemy.orm.sessionmaker`.
+Configurational arguments accepted by :func:`~sqlalchemy.orm.sessionmaker` and :func:`~sqlalchemy.orm.create_session` are the same as that of the :class:`~sqlalchemy.orm.session.Session` class itself, and are described at :func:`sqlalchemy.orm.sessionmaker`.
-Note that the defaults of ``create_session()`` are the opposite of that of ``sessionmaker()``: autoflush and expire_on_commit are False, autocommit is True. It is recommended to use the ``sessionmaker()`` function instead of ``create_session()``. ``create_session()`` is used to get a session with no automation turned on and is useful for testing.
+Note that the defaults of :func:`~sqlalchemy.orm.create_session` are the opposite of that of :func:`~sqlalchemy.orm.sessionmaker`: autoflush and expire_on_commit are False, autocommit is True. It is recommended to use the :func:`~sqlalchemy.orm.sessionmaker` function instead of :func:`~sqlalchemy.orm.create_session`. :func:`~sqlalchemy.orm.create_session` is used to get a session with no automation turned on and is useful for testing.
Using the Session
==================
* When do I make a ``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 ``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 ``Session()``, and the configuration of that session is controlled by that central point.
+ 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 ``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:`~sqlalchemy.orm.session.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:`~sqlalchemy.orm.session.Session` at the "class" level to the engine later on, using ``configure()``.
- In the examples in this section, we will frequently show the ``sessionmaker`` being created right above the line where we actually invoke ``Session()``. But that's just for example's sake ! In reality, the ``sessionmaker`` would be somewhere at the module level, and your individual ``Session()`` calls would be sprinkled all throughout your app, such as in a web application within each controller method.
+ In the examples in this section, we will frequently show the ``sessionmaker`` being created right above the line where we actually invoke :class:`~sqlalchemy.orm.session.Session()`. But that's just for example's sake ! In reality, the ``sessionmaker`` would be somewhere at the module level, and your individual :class:`~sqlalchemy.orm.session.Session()` calls would be sprinkled all throughout your app, such as in a web application within each controller method.
* When do I make a :class:`~sqlalchemy.orm.session.Session` ?
- You typically invoke ``Session()`` when you first need to talk to your database, and want to save some objects or load some existing ones. Then, you work with it, save your changes, and then dispose of it....or at the very least ``close()`` it. It's not a "global" kind of object, and should be handled more like a "local variable", as it's generally **not** safe to use with concurrent threads. Sessions are very inexpensive to make, and don't use any resources whatsoever until they are first used...so create some !
+ You typically invoke :class:`~sqlalchemy.orm.session.Session()` when you first need to talk to your database, and want to save some objects or load some existing ones. Then, you work with it, save your changes, and then dispose of it....or at the very least ``close()`` it. It's not a "global" kind of object, and should be handled more like a "local variable", as it's generally **not** safe to use with concurrent threads. Sessions are very inexpensive to make, and don't use any resources whatsoever until they are first used...so create some !
- There is also a pattern whereby you're using a **contextual session**, this is described later in :ref:`unitofwork_contextual`. In this pattern, a helper object is maintaining a :class:`~sqlalchemy.orm.session.Session` for you, most commonly one that is local to the current thread (and sometimes also local to an application instance). SQLAlchemy has worked this pattern out such that it still *looks* like you're creating a new session as you need one...so in that case, it's still a guaranteed win to just say ``Session()`` whenever you want a session.
+ There is also a pattern whereby you're using a **contextual session**, this is described later in :ref:`unitofwork_contextual`. In this pattern, a helper object is maintaining a :class:`~sqlalchemy.orm.session.Session` for you, most commonly one that is local to the current thread (and sometimes also local to an application instance). SQLAlchemy has worked this pattern out such that it still *looks* like you're creating a new session as you need one...so in that case, it's still a guaranteed win to just say :class:`~sqlalchemy.orm.session.Session()` whenever you want a session.
* Is the Session a cache ?
* How can I get the :class:`~sqlalchemy.orm.session.Session` for a certain object ?
- Use the ``object_session()`` classmethod available on :class:`~sqlalchemy.orm.session.Session`::
+ Use the :func:`~sqlalchemy.orm.session.Session.object_session` classmethod available on :class:`~sqlalchemy.orm.session.Session`::
session = Session.object_session(someobject)
Querying
--------
-The ``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. An entity is defined as a mapped class, a :class:`~sqlalchemy.orm.mapper.Mapper` object, an orm-enabled *descriptor*, or an ``AliasedClass`` object::
+The :class:`~sqlalchemy.orm.session.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. An entity is defined as a mapped class, a :class:`~sqlalchemy.orm.mapper.Mapper` object, an orm-enabled *descriptor*, or an ``AliasedClass`` object::
# query from a class
session.query(User).filter_by(name='ed').all()
session.commit() # write changes to the database
-To add a list of items to the session at once, use ``add_all()``::
+To add a list of items to the session at once, use :func:`~sqlalchemy.orm.session.Session.add_all`::
session.add_all([item1, item2, item3])
Merging
-------
-``merge()`` reconciles the current state of an instance and its associated children with existing data in the database, and returns a copy of the instance associated with the session. Usage is as follows::
+:func:`~sqlalchemy.orm.session.Session.merge` reconciles the current state of an instance and its associated children with existing data in the database, and returns a copy of the instance associated with the session. Usage is as follows::
merged_object = session.merge(existing_object)
* The operation is cascaded to associated child items along the ``merge`` cascade. Note that all changes present on the given instance, including changes to collections, are merged.
* The new instance is returned.
-With ``merge()``, the given instance is not placed within the session, and can be associated with a different session or detached. ``merge()`` is very useful for taking the state of any kind of object structure without regard for its origins or current session associations and placing that state within a session. Here's two examples:
+With :func:`~sqlalchemy.orm.session.Session.merge`, the given instance is not placed within the session, and can be associated with a different session or detached. :func:`~sqlalchemy.orm.session.Session.merge` is very useful for taking the state of any kind of object structure without regard for its origins or current session associations and placing that state within a session. Here's two examples:
- * An application which reads an object structure from a file and wishes to save it to the database might parse the file, build up the structure, and then use ``merge()`` to save it to the database, ensuring that the data within the file is used to formulate the primary key of each element of the structure. Later, when the file has changed, the same process can be re-run, producing a slightly different object structure, which can then be ``merged()`` in again, and the :class:`~sqlalchemy.orm.session.Session` will automatically update the database to reflect those changes.
+ * An application which reads an object structure from a file and wishes to save it to the database might parse the file, build up the structure, and then use :func:`~sqlalchemy.orm.session.Session.merge` to save it to the database, ensuring that the data within the file is used to formulate the primary key of each element of the structure. Later, when the file has changed, the same process can be re-run, producing a slightly different object structure, which can then be ``merged()`` in again, and the :class:`~sqlalchemy.orm.session.Session` will automatically update the database to reflect those changes.
* A web application stores mapped entities within an HTTP session object. When each request starts up, the serialized data can be merged into the session, so that the original entity may be safely shared among requests and threads.
-``merge()`` is frequently used by applications which implement their own second level caches. This refers to an application which uses an in memory dictionary, or an tool like Memcached to store objects over long running spans of time. When such an object needs to exist within a :class:`~sqlalchemy.orm.session.Session`, ``merge()`` is a good choice since it leaves the original cached object untouched. For this use case, merge provides a keyword option called ``load=False``. When this boolean flag is set to ``False``, ``merge()`` will not issue any SQL to reconcile the given object against the current state of the database, thereby reducing query overhead. The limitation is that the given object and all of its children may not contain any pending changes, and it's also of course possible that newer information in the database will not be present on the merged object, since no load is issued.
+:func:`~sqlalchemy.orm.session.Session.merge` is frequently used by applications which implement their own second level caches. This refers to an application which uses an in memory dictionary, or an tool like Memcached to store objects over long running spans of time. When such an object needs to exist within a :class:`~sqlalchemy.orm.session.Session`, :func:`~sqlalchemy.orm.session.Session.merge` is a good choice since it leaves the original cached object untouched. For this use case, merge provides a keyword option called ``load=False``. When this boolean flag is set to ``False``, :func:`~sqlalchemy.orm.session.Session.merge` will not issue any SQL to reconcile the given object against the current state of the database, thereby reducing query overhead. The limitation is that the given object and all of its children may not contain any pending changes, and it's also of course possible that newer information in the database will not be present on the merged object, since no load is issued.
Deleting
--------
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 :class:`~sqlalchemy.orm.query.Query` is issued, as well as within the ``commit()`` call before the transaction is committed. It also occurs before a SAVEPOINT is issued when ``begin_nested()`` is used.
-Regardless of the autoflush setting, a flush can always be forced by issuing ``flush()``::
+Regardless of the autoflush setting, a flush can always be forced by issuing :func:`~sqlalchemy.orm.session.Session.flush`::
session.flush()
-The "flush-on-Query" aspect of the behavior can be disabled by constructing ``sessionmaker()`` with the flag ``autoflush=False``::
+The "flush-on-Query" aspect of the behavior can be disabled by constructing :func:`~sqlalchemy.orm.sessionmaker` with the flag ``autoflush=False``::
Session = sessionmaker(autoflush=False)
Some autoflush-disable recipes are available at `DisableAutoFlush <http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DisableAutoflush>`_.
-The flush process *always* occurs within a transaction, even if the :class:`~sqlalchemy.orm.session.Session` has been configured with ``autocommit=True``, a setting that disables the session's persistent transactional state. If no transaction is present, ``flush()`` creates its own transaction and commits it. Any failures during flush will always result in a rollback of whatever transaction is present. If the Session is not in ``autocommit=True`` mode, an explicit call to ``rollback()`` is required after a flush fails, even though the underlying transaction will have been rolled back already - this is so that the overall nesting pattern of so-called "subtransactions" is consistently maintained.
+The flush process *always* occurs within a transaction, even if the :class:`~sqlalchemy.orm.session.Session` has been configured with ``autocommit=True``, a setting that disables the session's persistent transactional state. If no transaction is present, :func:`~sqlalchemy.orm.session.Session.flush` creates its own transaction and commits it. Any failures during flush will always result in a rollback of whatever transaction is present. If the Session is not in ``autocommit=True`` mode, an explicit call to ``rollback()`` is required after a flush fails, even though the underlying transaction will have been rolled back already - this is so that the overall nesting pattern of so-called "subtransactions" is consistently maintained.
Committing
----------
-``commit()`` is used to commit the current transaction. It always issues ``flush()`` beforehand to flush any remaining state to the database; this is independent of the "autoflush" setting. If no transaction is present, it raises an error. Note that the default behavior of the :class:`~sqlalchemy.orm.session.Session` is that a transaction is always present; this behavior can be disabled by setting ``autocommit=True``. In autocommit mode, a transaction can be initiated by calling the ``begin()`` method.
+``commit()`` is used to commit the current transaction. It always issues :func:`~sqlalchemy.orm.session.Session.flush` beforehand to flush any remaining state to the database; this is independent of the "autoflush" setting. If no transaction is present, it raises an error. Note that the default behavior of the :class:`~sqlalchemy.orm.session.Session` is that a transaction is always present; this behavior can be disabled by setting ``autocommit=True``. In autocommit mode, a transaction can be initiated by calling the ``begin()`` method.
-Another behavior of ``commit()`` is that by default it expires the state of all instances present after the commit is complete. This is so that when the instances are next accessed, either through attribute access or by them being present in a :class:`~sqlalchemy.orm.query.Query` result set, they receive the most recent state. To disable this behavior, configure ``sessionmaker()`` with ``expire_on_commit=False``.
+Another behavior of ``commit()`` is that by default it expires the state of all instances present after the commit is complete. This is so that when the instances are next accessed, either through attribute access or by them being present in a :class:`~sqlalchemy.orm.query.Query` result set, they receive the most recent state. To disable this behavior, configure :func:`~sqlalchemy.orm.sessionmaker` with ``expire_on_commit=False``.
Normally, instances loaded into the :class:`~sqlalchemy.orm.session.Session` are never changed by subsequent queries; the assumption is that the current transaction is isolated so the state most recently loaded is correct as long as the transaction continues. Setting ``autocommit=True`` works against this model to some degree since the :class:`~sqlalchemy.orm.session.Session` behaves in exactly the same way with regard to attribute state, except no transaction is present.
With that state understood, the :class:`~sqlalchemy.orm.session.Session` may safely continue usage after a rollback occurs.
-When a ``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 flush process always uses its own transactional demarcator called a *subtransaction*, which is described more fully in the docstrings for :class:`~sqlalchemy.orm.session.Session`. What it means here is that even though the database transaction has been rolled back, the end user must still issue ``rollback()`` to fully reset the state of the :class:`~sqlalchemy.orm.session.Session`.
+When a :func:`~sqlalchemy.orm.session.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 flush process always uses its own transactional demarcator called a *subtransaction*, which is described more fully in the docstrings for :class:`~sqlalchemy.orm.session.Session`. What it means here is that even though the database transaction has been rolled back, the end user must still issue ``rollback()`` to fully reset the state of the :class:`~sqlalchemy.orm.session.Session`.
Expunging
---------
Closing
-------
-The ``close()`` method issues a ``expunge_all()``, and releases any transactional/connection resources. When connections are returned to the connection pool, transactional state is rolled back as well.
+The ``close()`` method issues a :func:`~sqlalchemy.orm.session.Session.expunge_all`, and releases any transactional/connection resources. When connections are returned to the connection pool, transactional state is rolled back as well.
Refreshing / Expiring
---------------------
session.expire(obj1)
session.expire(obj2)
-``refresh()`` and ``expire()`` also support being passed a list of individual attribute names in which to be refreshed. These names can reference any attribute, column-based or relation based::
+:func:`~sqlalchemy.orm.session.Session.refresh` and :func:`~sqlalchemy.orm.session.Session.expire` also support being passed a list of individual attribute names in which to be refreshed. These names can reference any attribute, column-based or relation based::
# immediately re-load the attributes 'hello', 'world' on obj1, obj2
session.refresh(obj1, ['hello', 'world'])
session.expire(obj1, ['hello', 'world'])
session.expire(obj2, ['hello', 'world'])
-The full contents of the session may be expired at once using ``expire_all()``::
+The full contents of the session may be expired at once using :func:`~sqlalchemy.orm.session.Session.expire_all`::
session.expire_all()
-``refresh()`` and ``expire()`` are usually not needed when working with a default-configured :class:`~sqlalchemy.orm.session.Session`. The usual need is when an UPDATE or DELETE has been issued manually within the transaction using ``Session.execute()``.
+:func:`~sqlalchemy.orm.session.Session.refresh` and :func:`~sqlalchemy.orm.session.Session.expire` are usually not needed when working with a default-configured :class:`~sqlalchemy.orm.session.Session`. The usual need is when an UPDATE or DELETE has been issued manually within the transaction using ``Session.execute()``.
Session Attributes
------------------
# persistent objects that have been marked as deleted via session.delete(obj)
session.deleted
-Note that objects within the session are by default *weakly referenced*. This means that when they are dereferenced in the outside application, they fall out of scope from within the :class:`~sqlalchemy.orm.session.Session` as well and are subject to garbage collection by the Python interpreter. The exceptions to this include objects which are pending, objects which are marked as deleted, or persistent objects which have pending changes on them. After a full flush, these collections are all empty, and all objects are again weakly referenced. To disable the weak referencing behavior and force all objects within the session to remain until explicitly expunged, configure ``sessionmaker()`` with the ``weak_identity_map=False`` setting.
+Note that objects within the session are by default *weakly referenced*. This means that when they are dereferenced in the outside application, they fall out of scope from within the :class:`~sqlalchemy.orm.session.Session` as well and are subject to garbage collection by the Python interpreter. The exceptions to this include objects which are pending, objects which are marked as deleted, or persistent objects which have pending changes on them. After a full flush, these collections are all empty, and all objects are again weakly referenced. To disable the weak referencing behavior and force all objects within the session to remain until explicitly expunged, configure :func:`~sqlalchemy.orm.sessionmaker` with the ``weak_identity_map=False`` setting.
.. _unitofwork_cascades:
Mappers support the concept of configurable *cascade* behavior on :func:`~sqlalchemy.orm.relation()` constructs. This behavior controls how the Session should treat the instances that have a parent-child relationship with another instance that is operated upon by the Session. Cascade is indicated as a comma-separated list of string keywords, with the possible values ``all``, ``delete``, ``save-update``, ``refresh-expire``, ``merge``, ``expunge``, and ``delete-orphan``.
-Cascading is configured by setting the ``cascade`` keyword argument on a ``relation()``::
+Cascading is configured by setting the ``cascade`` keyword argument on a :func:`~sqlalchemy.orm.relation`::
mapper(Order, order_table, properties={
'items' : relation(Item, items_table, cascade="all, delete-orphan"),
``begin_nested()`` may be called any number of times, which will issue a new SAVEPOINT with a unique identifier for each call. For each ``begin_nested()`` call, a corresponding ``rollback()`` or ``commit()`` must be issued.
-When ``begin_nested()`` is called, a ``flush()`` is unconditionally issued (regardless of the ``autoflush`` setting). This is so that when a ``rollback()`` occurs, the full state of the session is expired, thus causing all subsequent attribute/instance access to reference the full state of the :class:`~sqlalchemy.orm.session.Session` right before ``begin_nested()`` was called.
+When ``begin_nested()`` is called, a :func:`~sqlalchemy.orm.session.Session.flush` is unconditionally issued (regardless of the ``autoflush`` setting). This is so that when a ``rollback()`` occurs, the full state of the session is expired, thus causing all subsequent attribute/instance access to reference the full state of the :class:`~sqlalchemy.orm.session.Session` right before ``begin_nested()`` was called.
Enabling Two-Phase Commit
-------------------------
-Finally, for MySQL, PostgreSQL, and soon Oracle as well, the session can be instructed to use two-phase commit semantics. This will coordinate the committing of transactions across databases so that the transaction is either committed or rolled back in all databases. You can also ``prepare()`` the session for interacting with transactions not managed by SQLAlchemy. To use two phase transactions set the flag ``twophase=True`` on the session::
+Finally, for MySQL, PostgreSQL, and soon Oracle as well, the session can be instructed to use two-phase commit semantics. This will coordinate the committing of transactions across databases so that the transaction is either committed or rolled back in all databases. You can also :func:`~sqlalchemy.orm.session.Session.prepare` the session for interacting with transactions not managed by SQLAlchemy. To use two phase transactions set the flag ``twophase=True`` on the session::
engine1 = create_engine('postgresql://db1')
engine2 = create_engine('postgresql://db2')
trans.commit() # commit the actual transaction
-Note that above, we issue a ``commit()`` both on the :class:`~sqlalchemy.orm.session.Session` as well as the :class:`~sqlalchemy.engine.base.Transaction`. This is an example of where we take advantage of :class:`~sqlalchemy.engine.base.Connection`'s ability to maintain *subtransactions*, or nested begin/commit pairs. The :class:`~sqlalchemy.orm.session.Session` is used exactly as though it were managing the transaction on its own; its ``commit()`` method issues its ``flush()``, and commits the subtransaction. The subsequent transaction the :class:`~sqlalchemy.orm.session.Session` starts after commit will not begin until it's next used. Above we issue a ``close()`` to prevent this from occurring. Finally, the actual transaction is committed using ``Transaction.commit()``.
+Note that above, we issue a ``commit()`` both on the :class:`~sqlalchemy.orm.session.Session` as well as the :class:`~sqlalchemy.engine.base.Transaction`. This is an example of where we take advantage of :class:`~sqlalchemy.engine.base.Connection`'s ability to maintain *subtransactions*, or nested begin/commit pairs. The :class:`~sqlalchemy.orm.session.Session` is used exactly as though it were managing the transaction on its own; its ``commit()`` method issues its :func:`~sqlalchemy.orm.session.Session.flush`, and commits the subtransaction. The subsequent transaction the :class:`~sqlalchemy.orm.session.Session` starts after commit will not begin until it's next used. Above we issue a ``close()`` to prevent this from occurring. Finally, the actual transaction is committed using ``Transaction.commit()``.
When using the ``threadlocal`` engine context, the process above is simplified; the :class:`~sqlalchemy.orm.session.Session` uses the same connection/transaction as everyone else in the current thread, whether or not you explicitly bind it::
Contextual/Thread-local Sessions
=================================
-A common need in applications, particularly those built around web frameworks, is the ability to "share" a :class:`~sqlalchemy.orm.session.Session` object among disparate parts of an application, without needing to pass the object explicitly to all method and function calls. What you're really looking for is some kind of "global" session object, or at least "global" to all the parts of an application which are tasked with servicing the current request. For this pattern, SQLAlchemy provides the ability to enhance the :class:`~sqlalchemy.orm.session.Session` class generated by ``sessionmaker()`` to provide auto-contextualizing support. This means that whenever you create a :class:`~sqlalchemy.orm.session.Session` instance with its constructor, you get an *existing* :class:`~sqlalchemy.orm.session.Session` object which is bound to some "context". By default, this context is the current thread. This feature is what previously was accomplished using the ``sessioncontext`` SQLAlchemy extension.
+A common need in applications, particularly those built around web frameworks, is the ability to "share" a :class:`~sqlalchemy.orm.session.Session` object among disparate parts of an application, without needing to pass the object explicitly to all method and function calls. What you're really looking for is some kind of "global" session object, or at least "global" to all the parts of an application which are tasked with servicing the current request. For this pattern, SQLAlchemy provides the ability to enhance the :class:`~sqlalchemy.orm.session.Session` class generated by :func:`~sqlalchemy.orm.sessionmaker` to provide auto-contextualizing support. This means that whenever you create a :class:`~sqlalchemy.orm.session.Session` instance with its constructor, you get an *existing* :class:`~sqlalchemy.orm.session.Session` object which is bound to some "context". By default, this context is the current thread. This feature is what previously was accomplished using the ``sessioncontext`` SQLAlchemy extension.
Creating a Thread-local Context
-------------------------------
-The ``scoped_session()`` function wraps around the ``sessionmaker()`` function, and produces an object which behaves the same as the :class:`~sqlalchemy.orm.session.Session` subclass returned by ``sessionmaker()``::
+The :func:`~sqlalchemy.orm.scoped_session` function wraps around the :func:`~sqlalchemy.orm.sessionmaker` function, and produces an object which behaves the same as the :class:`~sqlalchemy.orm.session.Session` subclass returned by :func:`~sqlalchemy.orm.sessionmaker`::
from sqlalchemy.orm import scoped_session, sessionmaker
Session = scoped_session(sessionmaker())
-However, when you instantiate this :class:`~sqlalchemy.orm.session.Session` "class", in reality the object is pulled from a threadlocal variable, or if it doesn't exist yet, it's created using the underlying class generated by ``sessionmaker()``::
+However, when you instantiate this :class:`~sqlalchemy.orm.session.Session` "class", in reality the object is pulled from a threadlocal variable, or if it doesn't exist yet, it's created using the underlying class generated by :func:`~sqlalchemy.orm.sessionmaker`::
>>> # call Session() the first time. the new Session instance is created.
>>> session = Session()
>>> session is session2
True
-Since the ``Session()`` constructor now returns the same :class:`~sqlalchemy.orm.session.Session` object every time within the current thread, the object returned by ``scoped_session()`` also implements most of the :class:`~sqlalchemy.orm.session.Session` methods and properties at the "class" level, such that you don't even need to instantiate ``Session()``::
+Since the :class:`~sqlalchemy.orm.session.Session()` constructor now returns the same :class:`~sqlalchemy.orm.session.Session` object every time within the current thread, the object returned by :func:`~sqlalchemy.orm.scoped_session` also implements most of the :class:`~sqlalchemy.orm.session.Session` methods and properties at the "class" level, such that you don't even need to instantiate :class:`~sqlalchemy.orm.session.Session()`::
# create some objects
u1 = User()
The above example illustrates an explicit call to ``Session.remove()``. This has the effect such that each web request starts fresh with a brand new session. When integrating with a web framework, there's actually many options on how to proceed for this step:
* Session.remove() - this is the most cut and dry approach; the :class:`~sqlalchemy.orm.session.Session` is thrown away, all of its transactional/connection resources are closed out, everything within it is explicitly gone. A new :class:`~sqlalchemy.orm.session.Session` will be used on the next request.
-* Session.close() - Similar to calling ``remove()``, in that all objects are explicitly expunged and all transactional/connection resources closed, except the actual :class:`~sqlalchemy.orm.session.Session` object hangs around. It doesn't make too much difference here unless the start of the web request would like to pass specific options to the initial construction of ``Session()``, such as a specific :class:`~sqlalchemy.engine.base.Engine` to bind to.
+* Session.close() - Similar to calling ``remove()``, in that all objects are explicitly expunged and all transactional/connection resources closed, except the actual :class:`~sqlalchemy.orm.session.Session` object hangs around. It doesn't make too much difference here unless the start of the web request would like to pass specific options to the initial construction of :class:`~sqlalchemy.orm.session.Session()`, such as a specific :class:`~sqlalchemy.engine.base.Engine` to bind to.
* Session.commit() - In this case, the behavior is that any remaining changes pending are flushed, and the transaction is committed. The full state of the session is expired, so that when the next web request is started, all data will be reloaded. In reality, the contents of the :class:`~sqlalchemy.orm.session.Session` are weakly referenced anyway so its likely that it will be empty on the next request in any case.
* Session.rollback() - Similar to calling commit, except we assume that the user would have called commit explicitly if that was desired; the ``rollback()`` ensures that no transactional state remains and expires all data, in the case that the request was aborted and did not roll back itself.
* do nothing - this is a valid option as well. The controller code is responsible for doing one of the above steps at the end of the request.
==========
-For this tutorial we will use an in-memory-only SQLite database. This is an easy way to test things without needing to have an actual database defined anywhere. To connect we use ``create_engine()``:
+For this tutorial we will use an in-memory-only SQLite database. This is an easy way to test things without needing to have an actual database defined anywhere. To connect we use :func:`~sqlalchemy.create_engine`:
.. sourcecode:: pycon+sql
All about how to define :class:`~sqlalchemy.schema.Table` objects, as well as how to create them from an existing database automatically, is described in :ref:`metadata_toplevel`.
-Next, to tell the :class:`~sqlalchemy.schema.MetaData` we'd actually like to create our selection of tables for real inside the SQLite database, we use ``create_all()``, passing it the ``engine`` instance which points to our database. This will check for the presence of each table first before creating, so it's safe to call multiple times:
+Next, to tell the :class:`~sqlalchemy.schema.MetaData` we'd actually like to create our selection of tables for real inside the SQLite database, we use :func:`~sqlalchemy.schema.MetaData.create_all`, passing it the ``engine`` instance which points to our database. This will check for the presence of each table first before creating, so it's safe to call multiple times:
.. sourcecode:: pycon+sql
VARCHAR columns were generated without a length; on SQLite and Postgresql,
this is a valid datatype, but on others, it's not allowed. So if running
this tutorial on one of those databases, and you wish to use SQLAlchemy to
- issue CREATE TABLE, a "length" may be provided to the ``String`` type as
+ issue CREATE TABLE, a "length" may be provided to the :class:`~sqlalchemy.types.String` type as
below::
Column('name', String(50))
- The length field on ``String``, as well as similar precision/scale fields
- available on ``Integer``, ``Numeric``, etc. are not referenced by
+ The length field on :class:`~sqlalchemy.types.String`, as well as similar precision/scale fields
+ available on :class:`~sqlalchemy.types.Integer`, :class:`~sqlalchemy.types.Numeric`, etc. are not referenced by
SQLAlchemy other than when creating tables.
Additionally, Firebird and Oracle require sequences to generate new
FROM users
[]
-Above, we issued a basic ``select()`` call, placing the ``users`` table within the COLUMNS clause of the select, and then executing. SQLAlchemy expanded the ``users`` table into the set of each of its columns, and also generated a FROM clause for us. The result returned is again a :class:`~sqlalchemy.engine.base.ResultProxy` object, which acts much like a DBAPI cursor, including methods such as ``fetchone()`` and ``fetchall()``. The easiest way to get rows from it is to just iterate:
+Above, we issued a basic ``select()`` call, placing the ``users`` table within the COLUMNS clause of the select, and then executing. SQLAlchemy expanded the ``users`` table into the set of each of its columns, and also generated a FROM clause for us. The result returned is again a :class:`~sqlalchemy.engine.base.ResultProxy` object, which acts much like a DBAPI cursor, including methods such as :func:`~sqlalchemy.engine.base.ResultProxy.fetchone` and :func:`~sqlalchemy.engine.base.ResultProxy.fetchall`. The easiest way to get rows from it is to just iterate:
.. sourcecode:: pycon+sql
>>> print users.c.id + addresses.c.id
users.id + addresses.id
-Interestingly, the type of the :class:`~sqlalchemy.schema.Column` is important ! If we use ``+`` with two string based columns (recall we put types like ``Integer`` and ``String`` on our :class:`~sqlalchemy.schema.Column` objects at the beginning), we get something different:
+Interestingly, the type of the :class:`~sqlalchemy.schema.Column` is important ! If we use ``+`` with two string based columns (recall we put types like :class:`~sqlalchemy.types.Integer` and :class:`~sqlalchemy.types.String` on our :class:`~sqlalchemy.schema.Column` objects at the beginning), we get something different:
.. sourcecode:: pycon+sql
['jack', '%@msn.com']
{stop}[(1, u'jack', u'Jack Jones', 1, 1, u'jack@yahoo.com'), (1, u'jack', u'Jack Jones', 2, 1, u'jack@msn.com')]
-So we started small, added one little thing at a time, and at the end we have a huge statement..which actually works. Now let's do one more thing; the searching function wants to add another ``email_address`` criterion on, however it doesn't want to construct an alias of the ``addresses`` table; suppose many parts of the application are written to deal specifically with the ``addresses`` table, and to change all those functions to support receiving an arbitrary alias of the address would be cumbersome. We can actually *convert* the ``addresses`` table within the *existing* statement to be an alias of itself, using ``replace_selectable()``:
+So we started small, added one little thing at a time, and at the end we have a huge statement..which actually works. Now let's do one more thing; the searching function wants to add another ``email_address`` criterion on, however it doesn't want to construct an alias of the ``addresses`` table; suppose many parts of the application are written to deal specifically with the ``addresses`` table, and to change all those functions to support receiving an arbitrary alias of the address would be cumbersome. We can actually *convert* the ``addresses`` table within the *existing* statement to be an alias of itself, using :func:`~sqlalchemy.sql.expression.FromClause.replace_selectable`:
.. sourcecode:: pycon+sql
FROM calculate(:x, :y))
WHERE users.id > z
-If we wanted to use our ``calculate`` statement twice with different bind parameters, the ``unique_params()`` function will create copies for us, and mark the bind parameters as "unique" so that conflicting names are isolated. Note we also make two separate aliases of our selectable:
+If we wanted to use our ``calculate`` statement twice with different bind parameters, the :func:`~sqlalchemy.sql.expression.ClauseElement.unique_params` function will create copies for us, and mark the bind parameters as "unique" so that conflicting names are isolated. Note we also make two separate aliases of our selectable:
.. sourcecode:: pycon+sql
--------------
-To embed a SELECT in a column expression, use ``as_scalar()``:
+To embed a SELECT in a column expression, use :func:`~sqlalchemy.sql.expression._SelectBaseMixin.as_scalar`:
.. sourcecode:: pycon+sql