- classical mappings

- break out eager loads add contains eager again
- edits
This commit is contained in:
Mike Bayer
2011-08-07 01:13:29 -04:00
parent 6920205d1e
commit c4e3cb2bf5
5 changed files with 320 additions and 186 deletions
+6
View File
@@ -15,6 +15,11 @@ When mappers are configured in an inheritance relationship, SQLAlchemy has the
ability to load elements "polymorphically", meaning that a single query can
return objects of multiple types.
.. note:: This section currently uses classical mappings to illustrate inheritance
configurations, and will soon be updated to standardize on Declarative.
Until then, please refer to :ref:`declarative_inheritance` for information on
how common inheritance mappings are constructed declaratively.
For the following sections, assume this class relationship:
.. sourcecode:: python+sql
@@ -525,6 +530,7 @@ Upon select, the polymorphic union produces a query like this:
For a recipe that sets up concrete inheritance using declarative, see the `DeclarativeAbstractConcreteBase
<http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DeclarativeAbstractConcreteBase>`_ recipe on the wiki.
Other helpers for concrete inheritance with declarative are currently under development.
Using Relationships with Inheritance
------------------------------------
+128 -47
View File
@@ -10,9 +10,89 @@ This section describes a variety of configurational patterns that are usable
with mappers. It assumes you've worked through :ref:`ormtutorial_toplevel` and
know how to construct and use rudimentary mappers and relationships.
Note that all patterns here apply both to the usage of explicit
:func:`~.orm.mapper` and :class:`.Table` objects as well as when using the
:mod:`sqlalchemy.ext.declarative` extension. Any example in this section which
.. _classical_mapping:
Classical Mappings
==================
Recall from :ref:`ormtutorial_toplevel` that we normally use the :ref:`declarative_toplevel`
system to define mappings. The "Classical Mapping" refers to the process
of defining :class:`.Table` metadata and mapped class via :func:`.mapper` separately.
While direct usage of :func:`.mapper` is not prominent in modern SQLAlchemy,
the function can be used
to create alternative mappings for an already mapped class, offers greater configurational
flexibility in certain highly circular configurations, and is also at the base of
alternative configurational systems not based upon Declarative. Many SQLAlchemy
applications in fact use classical mappings directly for configuration.
The ``User`` example in the tutorial, using classical mapping, defines the
:class:`.Table`, class, and :func:`.mapper` of the class separately. Below we illustrate
the full ``User``/``Address`` example using this style::
from sqlalchemy import Table, Metadata, Column, ForeignKey, Integer, String
from sqlalchemy.orm import mapper, relationship
metadata = MetaData()
users = Table('user', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('fullname', String(50)),
Column('password', String(12))
)
addresses = Table('address', metadata,
Column('id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey('user.id')),
Column('email_address', String(50))
)
class User(object):
def __init__(self, name, fullname, password):
self.name = name
self.fullname = fullname
self.password = password
class Address(object):
def __init__(self, email_address):
self.email_address = email_address
mapper(User, users, properties={
'addresses':relationship(Address, order_by=addresses.c.id, backref="user")
})
mapper(Address, addresses)
When the above is complete we now have a :class:`.Table`/:func:`.mapper` setup the same as that
set up using Declarative in the tutorial. Note that the mappings do not have the
benefit of the instrumented ``User`` and ``Address`` classes available, nor is the "string" argument
system of :func:`.relationship` available, as this is a feature of Declarative. The ``order_by``
argument of the ``User.addresses`` relationship is defined in terms of the actual ``addresses``
table instead of the ``Address`` class.
It's also worth noting that the "Classical" and "Declarative" mapping systems are not in
any way exclusive of each other. The two can be mixed freely - below we can
define a new class ``Order`` using a declarative base, which links back to ``User``-
no problem, except that we can't specify ``User`` as a string since it's not available
in the "base" registry::
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Order(Base):
__tablename__ = 'order'
id = Column(Integer, primary_key=True)
user_id = Column(ForeignKey('user.id'))
order_number = Column(String(50))
user = relationship(User, backref="orders")
This reference document currently uses classical mappings for most examples. However, all
patterns here apply both to the usage of explicit
:func:`~.orm.mapper` and :class:`.Table` objects as well as when using
Declarative, where options that are specific to the :func:`.mapper`
function can be specified with Declarative via the ``__mapper__`` attribute. Any example in this section which
takes a form such as::
mapper(User, users_table, primary_key=[users_table.c.id])
@@ -25,8 +105,8 @@ Would translate into declarative as::
'primary_key':[users_table.c.id]
}
Or if using ``__tablename__``, :class:`.Column` objects are declared inline
with the class definition. These are usable as is within ``__mapper_args__``::
:class:`.Column` objects which are declared inline can also
be used directly in ``__mapper_args__``::
class User(Base):
__tablename__ = 'users'
@@ -45,48 +125,6 @@ The default behavior of :func:`~.orm.mapper` is to assemble all the columns in
the mapped :class:`.Table` into mapped object attributes. This behavior can be
modified in several ways, as well as enhanced by SQL expressions.
Mapping a Subset of Table Columns
---------------------------------
To reference a subset of columns referenced by a table as mapped attributes,
use the ``include_properties`` or ``exclude_properties`` arguments. For
example::
mapper(User, users_table, include_properties=['user_id', 'user_name'])
...will map the ``User`` class to the ``users_table`` table, only including
the "user_id" and "user_name" columns - the rest are not refererenced.
Similarly::
mapper(Address, addresses_table,
exclude_properties=['street', 'city', 'state', 'zip'])
...will map the ``Address`` class to the ``addresses_table`` table, including
all columns present except "street", "city", "state", and "zip".
When this mapping is used, the columns that are not included will not be
referenced in any SELECT statements emitted by :class:`.Query`, nor will there
be any mapped attribute on the mapped class which represents the column;
assigning an attribute of that name will have no effect beyond that of
a normal Python attribute assignment.
In some cases, multiple columns may have the same name, such as when
mapping to a join of two or more tables that share some column name. To
exclude or include individual columns, :class:`.Column` objects
may also be placed within the "include_properties" and "exclude_properties"
collections (new feature as of 0.6.4)::
mapper(UserAddress, users_table.join(addresses_table),
exclude_properties=[addresses_table.c.id],
primary_key=[users_table.c.id]
)
It should be noted that insert and update defaults configured on individal
:class:`.Column` objects, such as those configured by the "default",
"update", "server_default" and "server_onupdate" arguments, will continue
to function normally even if those :class:`.Column` objects are not mapped.
This functionality is part of the SQL expression and execution system and
occurs below the level of the ORM.
Attribute Names for Mapped Columns
@@ -168,6 +206,49 @@ or with declarative::
Further examples of :func:`.column_property` are at :ref:`mapper_sql_expressions`.
Mapping a Subset of Table Columns
---------------------------------
To reference a subset of columns referenced by a table as mapped attributes,
use the ``include_properties`` or ``exclude_properties`` arguments. For
example::
mapper(User, users_table, include_properties=['user_id', 'user_name'])
...will map the ``User`` class to the ``users_table`` table, only including
the "user_id" and "user_name" columns - the rest are not refererenced.
Similarly::
mapper(Address, addresses_table,
exclude_properties=['street', 'city', 'state', 'zip'])
...will map the ``Address`` class to the ``addresses_table`` table, including
all columns present except "street", "city", "state", and "zip".
When this mapping is used, the columns that are not included will not be
referenced in any SELECT statements emitted by :class:`.Query`, nor will there
be any mapped attribute on the mapped class which represents the column;
assigning an attribute of that name will have no effect beyond that of
a normal Python attribute assignment.
In some cases, multiple columns may have the same name, such as when
mapping to a join of two or more tables that share some column name. To
exclude or include individual columns, :class:`.Column` objects
may also be placed within the "include_properties" and "exclude_properties"
collections (new feature as of 0.6.4)::
mapper(UserAddress, users_table.join(addresses_table),
exclude_properties=[addresses_table.c.id],
primary_key=[users_table.c.id]
)
It should be noted that insert and update defaults configured on individal
:class:`.Column` objects, such as those configured by the "default",
"update", "server_default" and "server_onupdate" arguments, will continue
to function normally even if those :class:`.Column` objects are not mapped.
This functionality is part of the SQL expression and execution system and
occurs below the level of the ORM.
.. autofunction:: column_property
.. _deferred:
+184 -137
View File
@@ -70,13 +70,11 @@ the SQL behind a popup window so it doesn't get in our way; just click the
The return value of :func:`.create_engine` is an instance of :class:`.Engine`, and it represents
the core interface to the database, adapted through a **dialect** that handles the details
of the database and DBAPI in use. In this case the SQLite dialect will interpret instructions
to the Python built-in ``sqlite3`` module.
to the Python built-in ``sqlite3`` module.
The :class:`.Engine` has not actually tried to connect to the database yet; that happens
only the first time it is asked to perform a task against the database.
Normally, the :class:`.Engine` is passed off to the ORM where it is used behind the scenes.
We can execute SQL directly from it however, as we illustrate here:
only the first time it is asked to perform a task against the database. We can illustrate
this by asking it to perform a simple SELECT statement:
.. sourcecode:: python+sql
@@ -85,24 +83,28 @@ We can execute SQL directly from it however, as we illustrate here:
()
{stop}1
Above, by executing a SQL statement, the :class:`.Engine` creates a connection which
by default remains pooled, and will be re-used for subsequent statement executions.
As the :meth:`.Engine.execute` method is called, the :class:`.Engine` establishes a connection to the
SQLite database, which is then used to emit the SQL. The connection is then returned to an internal
connection pool where it will be reused on subsequent statement executions. While we illustrate direct usage of the
:class:`.Engine` here, this isn't typically necessary when using the ORM, where the :class:`.Engine`,
once created, is used behind the scenes by the ORM as we'll see shortly.
Declare a Mapping
=================
When using the ORM, the basic configurational process is to define **table metadata**,
which describes the database tables we'll be dealing with, and then to define
our own classes which will be **mapped** to those tables. In modern SQLAlchemy,
When using the ORM, the configurational process starts by describing the database
tables we'll be dealing with, and then by defining our own classes which will
be mapped to those tables. In modern SQLAlchemy,
these two tasks are usually performed together,
using a system known as :ref:`declarative_toplevel`, which allows us to create our own
mapped classes that also define how they will be mapped to an actual database
table.
using a system known as :ref:`declarative_toplevel`, which allows us to create
classes that include directives to describe the actual database table they will
be mapped to.
Mapped classes are defined in terms of a base class which maintains a catalog of classes and
Classes mapped using the Declarative system are defined in terms of a base class which
maintains a catalog of classes and
tables relative to that base - this is known as the **declarative base class**. Our
application will usually have just one instance of this base in a commonly
imported module. We create this class using the :func:`.declarative_base`
imported module. We create the base class using the :func:`.declarative_base`
function, as follows::
>>> from sqlalchemy.ext.declarative import declarative_base
@@ -111,11 +113,11 @@ function, as follows::
Now that we have a "base", we can define any number of mapped classes in terms
of it. We will start with just a single table called ``users``, which will store
records for the end-users using our application (lets assume it's a website).
records for the end-users using our application.
A new class called ``User`` will be the class to which we map this table. The
imports we'll need to accomplish this include objects that represent the components
of our table, including the :class:`.Column` class which represents a column
in a table, as well as the :class:`.Integer` and :class:`.String` type objects that
of our table, including the :class:`.Column` class which represents a database column,
as well as the :class:`.Integer` and :class:`.String` classes that
represent basic datatypes used in columns::
>>> from sqlalchemy import Column, Integer, String
@@ -135,26 +137,43 @@ represent basic datatypes used in columns::
... def __repr__(self):
... return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)
The above ``User`` class establishes details about the table being mapped, including the name of the table denoted
by the ``__tablename__`` attribute, a set of columns ``id``, ``name``, ``fullname`` and ``password``,
where the ``id`` column will also be the primary key of the table. While its certainly possible
that some database tables don't have primary key columns (as is also the case with views, which can
also be mapped), the ORM in order to actually map to a particular table needs there
to be at least one column denoted as a primary key column; multiple-column, i.e. composite, primary keys
are of course entirely feasible as well.
We define a constructor via ``__init__()`` and also a ``__repr__()`` method - both are optional. The
class of course can have any number of other methods and attributes as required by the application,
as it's basically just a plain Python class. Inheriting from ``Base`` is also only a requirement
of the declarative configurational system, which itself is optional and relatively open ended; at it's
core, the SQLAlchemy ORM only requires that a class be a so-called "new style class", that is, it inherits
from ``object`` in Python 2, in order to be mapped. All classes in Python 3 are "new style" classes.
.. topic:: The Non Opinionated Philosophy
Above, the ``__tablename__`` attribute represents the name of the table in the
database to which we are mapping, and the :class:`.Column` object referenced by the
name ``id`` defines the **primary key** column of our table; the usage of :class:`.Integer`
states that it should be of type ``INT``. SQLAlchemy never makes
assumptions about decisions like these - the developer using SQLAlchemy must
always decide on the specific conventions to be used. However, that doesn't mean the
In our ``User`` mapping example, it was required that we identify the name of the table
in use, as well as the names and characteristics of all columns which we care about,
including which column or columns
represent the primary key, as well as some basic information about the types in use.
SQLAlchemy never makes assumptions about these decisions - the developer must
always be explicit about specific conventions in use. However, that doesn't mean the
task can't be automated. While this tutorial will keep things explicit, developers are
encouraged to make usage of helper functions as well as "Declarative Mixins" to
encouraged to make use of helper functions as well as "Declarative Mixins" to
automate their tasks in large scale applications. The section :ref:`declarative_mixins`
introduces many of these techniques.
With our ``User`` class constructed via the Declarative system, we have defined **table metadata** as well as a
**mapped class**. This configuration is shorthand for what in SQLAlchemy is
called a "classical mapping",
which would have us first create an object representing the 'users' table using a class known as
:class:`.Table`, and then creating a mapping to this table through the usage of a function called
:func:`.mapper`. Declarative instead performs these steps for us, making available the
:class:`.Table` it has created for us via the ``__table__`` attribute::
With our ``User`` class constructed via the Declarative system, we have defined information about
our table, known as **table metadata**, as well as a user-defined class which is linked to this
table, known as a **mapped class**. Declarative has provided for us a shorthand system for what in SQLAlchemy is
called a "Classical Mapping", which specifies these two units separately and is discussed
in :ref:`classical_mapping`. The table
is actually represented by a datastructure known as :class:`.Table`, and the mapping represented
by a "mapper" object generated by a function called :func:`.mapper`. Declarative performs both of
these steps for us, making available the
:class:`.Table` it has created via the ``__table__`` attribute::
>>> User.__table__ # doctest: +NORMALIZE_WHITESPACE
Table('users', MetaData(None),
@@ -175,12 +194,14 @@ new tables that have yet to be created in our SQLite database, so one helpful fe
the :class:`.MetaData` object offers is the ability to issue CREATE TABLE statements
to the database for all tables that don't yet exist. We illustrate this
by calling the :meth:`.MetaData.create_all` method, passing in our :class:`.Engine`
as a source of database connectivity:
as a source of database connectivity. We will see that special commands are
first emitted to check for the presence of the ``users`` table, and following that
the actual ``CREATE TABLE`` statement:
.. sourcecode:: python+sql
{sql}>>> Base.metadata.create_all(engine) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
PRAGMA table_info("users")
>>> Base.metadata.create_all(engine) # doctest:+ELLIPSIS,+NORMALIZE_WHITESPACE
{opensql}PRAGMA table_info("users")
()
CREATE TABLE users (
id INTEGER NOT NULL,
@@ -241,8 +262,7 @@ as a source of database connectivity:
Create an Instance of the Mapped Class
======================================
With our fully specified ``User`` class and associated table metadata,
let's now create and inspect a ``User`` object::
With mappings complete, let's now create and inspect a ``User`` object::
>>> ed_user = User('ed', 'Ed Jones', 'edspassword')
>>> ed_user.name
@@ -259,8 +279,8 @@ default, the ORM creates class attributes for all columns present
in the table being mapped. These class attributes exist as
`Python descriptors <http://docs.python.org/howto/descriptor.html>`_, and
define **instrumentation** for the mapped class. The
functionality of this instrumentation is very rich and includes the ability to
track modifications and automatically load new data from the database when
functionality of this instrumentation includes the ability to fire on change
events, track modifications, and to automatically load new data from the database when
needed.
Since we have not yet told SQLAlchemy to persist ``Ed Jones`` within the
@@ -271,9 +291,10 @@ will be populated with a newly generated value.
Note that in our ``User`` example we supplied an ``__init__()`` method,
which receives ``name``, ``fullname`` and ``password`` as positional arguments.
The declarative system supplies for us a default constructor if one is
The Declarative system supplies for us a default constructor if one is
not already present, which accepts keyword arguments of the same name
as that of the mapped attributes::
as that of the mapped attributes. Below we define ``User`` without
specifying a constructor::
class User(Base):
__tablename__ = 'users'
@@ -335,7 +356,7 @@ To persist our ``User`` object, we :meth:`~.Session.add` it to our :class:`~sqla
>>> ed_user = User('ed', 'Ed Jones', 'edspassword')
>>> session.add(ed_user)
At this point, we say that the instance is *pending*; no SQL has yet been issued
At this point, we say that the instance is **pending**; no SQL has yet been issued
and the object is not yet represented by a row in the database. The
:class:`~sqlalchemy.orm.session.Session` will issue the SQL to persist ``Ed
Jones`` as soon as is needed, using a process known as a **flush**. If we
@@ -454,8 +475,7 @@ load-on-first-access. In this case, the entire row was re-loaded on access
because a new transaction was begun after we issued :meth:`~.Session.commit`. SQLAlchemy
by default refreshes data from a previous transaction the first time it's
accessed within a new transaction, so that the most recent state is available.
The level of reloading is configurable as is described in the chapter on
Sessions.
The level of reloading is configurable as is described in :ref:`session_toplevel`.
Rolling Back
============
@@ -641,7 +661,7 @@ operators with the class-level attributes on your mapped class:
('Ed Jones',)
{stop}ed
The :class:`~sqlalchemy.orm.query.Query` object is fully *generative*, meaning
The :class:`~sqlalchemy.orm.query.Query` object is fully **generative**, meaning
that most method calls return a new :class:`~sqlalchemy.orm.query.Query`
object upon which further criteria may be added. For example, to query for
users named "ed" with a full name of "Ed Jones", you can call
@@ -922,7 +942,7 @@ declarative, we define this table along with its mapped class, ``Address``:
... email_address = Column(String, nullable=False)
... user_id = Column(Integer, ForeignKey('users.id'))
...
... user = relationship(User, backref=backref('addresses', order_by=id))
... user = relationship("User", backref=backref('addresses', order_by=id))
...
... def __init__(self, email_address):
... self.email_address = email_address
@@ -930,48 +950,36 @@ declarative, we define this table along with its mapped class, ``Address``:
... def __repr__(self):
... return "<Address('%s')>" % self.email_address
The above class introduces a **foreign key** constraint which references the
``users`` table. This defines for SQLAlchemy the relationship between the two
tables at the database level. The relationship between the ``User`` and
``Address`` classes is defined separately using the
The above class introduces a **foreign key** constraint, applied to the
:class:`.Column` named ``user_id``, using the :class:`.ForeignKey` construct.
This configuration defines the relationship between the two tables
as it exists in the database. The relationship between the ``User`` and
``Address`` classes themselves is defined separately using the
:func:`~sqlalchemy.orm.relationship()` function, which defines an attribute
``user`` to be placed on the ``Address`` class, as well as an ``addresses``
collection to be placed on the ``User`` class. Such a relationship is known as
a **bidirectional** relationship. Because of the placement of the foreign key,
from ``Address`` to ``User`` it is **many to one**, and from ``User`` to
``Address`` it is **one to many**. SQLAlchemy is automatically aware of
many-to-one/one-to-many based on foreign keys.
``user`` to be placed on the ``Address`` class which links to ``User``, as well as an ``addresses``
collection to be placed on the ``User`` class, defined by the :func:`.backref`
class as a configuration nested inside the relationship. Such a relationship is known as
a **bidirectional** relationship, in that each of ``User`` and ``Address`` contains
an explicit link to the other.
.. note:: The :func:`~sqlalchemy.orm.relationship()` function has historically
been known as :func:`~sqlalchemy.orm.relation()` in the 0.5 series of
SQLAlchemy and earlier.
Based on the placement of the :class:`.ForeignKey`, :func:`.relationship` and
:func:`.backref` know that the ``User.addresses`` relationship is **one to many**
and that the ``Address.user`` relationship is **many to one** - this is established
strictly based on the geometry of the tables involved.
The :func:`~sqlalchemy.orm.relationship()` function is extremely flexible, and
could just have easily been defined on the ``User`` class:
.. sourcecode:: python+sql
Arguments to :func:`.relationship` which concern the remote class
can be specified using strings, which are evaluated once all mappings are complete
as Python expressions. The names which are allowed include, among other things, the names of all classes
which have been created in terms of the declared base. Below we illustrate creating
the same "addresses/user" bidirectional relationship in terms of ``User`` instead of
``Address``::
class User(Base):
# ....
addresses = relationship(Address, order_by=Address.id, backref="user")
We are also free to not define a backref, and to define the
:func:`~sqlalchemy.orm.relationship()` only on one class and not the other. It
is also possible to define two separate :func:`~sqlalchemy.orm.relationship()`
constructs for either direction, which is generally safe for many-to-one and
one-to-many relationships, but not for many-to-many relationships.
When using the :mod:`~.sqlalchemy.ext.declarative` extension,
:func:`~sqlalchemy.orm.relationship()` gives us the option to use strings for
most arguments that concern the target class, in the case that the target
class has not yet been defined:
.. sourcecode:: python+sql
class User(Base):
....
addresses = relationship("Address", order_by="Address.id", backref="user")
See the docstring for :func:`.relationship` for more detail on argument style.
We'll need to create the ``addresses`` table in the database, so we will issue
another CREATE from our metadata, which will skip over tables which have
already been created:
@@ -1015,9 +1023,9 @@ just assign a full list directly:
>>> jack.addresses = [Address(email_address='jack@google.com'), Address(email_address='j25@yahoo.com')]
When using a bidirectional relationship, elements added in one direction
automatically become visible in the other direction. This is the basic
behavior of the **backref** keyword, which maintains the relationship purely
in memory, without using any SQL:
automatically become visible in the other direction. This behavior occurs
based on attribute on-change events and is evaluated in Python, without
using any SQL:
.. sourcecode:: python+sql
@@ -1083,8 +1091,9 @@ Querying with Joins
Now that we have two tables, we can show some more features of :class:`.Query`,
specifically how to create queries that deal with both tables at the same time.
For those needing to brush up on this topic, the `Wikipedia page on SQL JOIN
<http://en.wikipedia.org/wiki/Join_%28SQL%29>`_ is a good place check.
The `Wikipedia page on SQL JOIN
<http://en.wikipedia.org/wiki/Join_%28SQL%29>`_ offers a good introduction to
join techniques, several of which we'll illustrate here.
To construct a simple implicit join between ``User`` and ``Address``,
we can use :meth:`.Query.filter()` to equate their related columns together.
@@ -1107,8 +1116,8 @@ Below we load the ``User`` and ``Address`` entities at once using this method:
('jack@google.com',)
{stop}<User('jack','Jack Bean', 'gjffdd')> <Address('jack@google.com')>
Next, to construct the actual SQL JOIN syntax, the most common way is
to use :meth:`.Query.join`:
The actual SQL JOIN syntax, on the other hand, is most easily achieved using the :meth:`.Query.join`
method:
.. sourcecode:: python+sql
@@ -1139,7 +1148,8 @@ As you would expect, the same idea is used for "outer" joins, using the
query.outerjoin(User.addresses) # LEFT OUTER JOIN
The reference documentation for :meth:`~.Query.join` contains detailed information
and examples of the calling styles accepted.
and examples of the calling styles accepted by this method; :meth:`~.Query.join`
is an important method at the center of usage for any SQL-fluent application.
Using Aliases
-------------
@@ -1148,7 +1158,7 @@ When querying across multiple tables, if the same table needs to be referenced
more than once, SQL typically requires that the table be *aliased* with
another name, so that it can be distinguished against other occurrences of
that table. The :class:`~sqlalchemy.orm.query.Query` supports this most
explicitly using the ``aliased`` construct. Below we join to the ``Address``
explicitly using the :attr:`~sqlalchemy.orm.aliased` construct. Below we join to the ``Address``
entity twice, to locate a user who has two distinct email addresses at the
same time:
@@ -1361,12 +1371,15 @@ Recall earlier that we illustrated a **lazy loading** operation, when
we accessed the ``User.addresses`` collection of a ``User`` and SQL
was emitted. If you want to reduce the number of queries (dramatically, in many cases),
we can apply an **eager load** to the query operation. SQLAlchemy
offers two types of eager loading, available via the :func:`.orm.joinedload`
and :func:`.orm.subqueryload` functions. These functions are **query options**
that give additional instructions to the :class:`.Query` on how
we would like various attributes to be loaded.
offers three types of eager loading, two of which are automatic, and a third
which involves custom criterion. All three are usually invoked via functions known
as **query options** which give additional instructions to the :class:`.Query` on how
we would like various attributes to be loaded, via the :meth:`.Query.options` method.
In this case we'd like to indicate that ``User.addresses`` should load eagerly.
Subquery Load
-------------
In this case we'd like to indicate that ``User.addresses`` should load eagerly.
A good choice for loading a set of objects as well as their related collections
is the :func:`.orm.subqueryload` option, which emits a second SELECT statement
that fully loads the collections associated with the results just loaded.
@@ -1403,7 +1416,10 @@ very easy to use:
>>> jack.addresses
[<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
The other eager loading function is more well known and is called
Joined Load
-------------
The other automatic eager loading function is more well known and is called
:func:`.orm.joinedload`. This style of loading emits a JOIN, by default
a LEFT OUTER JOIN, so that the lead object as well as the related object
or collection is loaded in one step. We illustrate loading the same
@@ -1445,31 +1461,7 @@ While :func:`.joinedload` has been around for a long time, :func:`.subqueryload`
is a newer form of eager loading. :func:`.subqueryload` tends to be more appropriate
for loading related collections while :func:`.joinedload` tends to be better suited
for many-to-one relationships, due to the fact that only one row is loaded
for both the lead and the related object. Below we illustrate loading an ``Address``
row, using :func:`.joinedload` to load the corresponding ``Address.user`` datamember:
.. sourcecode:: python+sql
{sql}>>> jacks_address = session.query(Address).\
... options(joinedload(Address.user)).\
... filter_by(email_address='j25@yahoo.com').\
... one() #doctest: +NORMALIZE_WHITESPACE
SELECT addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id,
users_1.id AS users_1_id,
users_1.name AS users_1_name,
users_1.fullname AS users_1_fullname,
users_1.password AS users_1_password
FROM addresses LEFT OUTER JOIN users AS users_1 ON users_1.id = addresses.user_id
WHERE addresses.email_address = ?
('j25@yahoo.com',)
{stop}>>> jacks_address
<Address('j25@yahoo.com')>
>>> jacks_address.user
<User('jack','Jack Bean', 'gjffdd')>
for both the lead and the related object.
.. topic:: ``joinedload()`` is not a replacement for ``join()``
@@ -1483,8 +1475,46 @@ row, using :func:`.joinedload` to load the corresponding ``Address.user`` datame
on actual results. See the section :ref:`zen_of_eager_loading` for
a detailed description of how this is used.
Full information on built in eager loading, as well as how to construct joined eager
loads explicitly using :meth:`.Query.join`, is available at :ref:`loading_toplevel`.
Explicit Join + Eagerload
--------------------------
A third style of eager loading is when we are constructing a JOIN explicitly in
order to locate the primary rows, and would like to additionally apply the extra
table to a related object or collection on the primary object. This feature
is supplied via the :func:`.orm.contains_eager` function, and is most
typically useful for pre-loading the many-to-one object on a query that needs
to filter on that same object. Below we illustrate loading an ``Address``
row as well as the related ``User`` object, filtering on the ``User`` named
"jack" and using :func:`.orm.contains_eager` to apply the "user" columns to the ``Address.user``
attribute:
.. sourcecode:: python+sql
>>> from sqlalchemy.orm import contains_eager
{sql}>>> jacks_addresses = session.query(Address).\
... join(Address.user).\
... filter(User.name=='jack').\
... options(contains_eager(Address.user)).\
... all() #doctest: +NORMALIZE_WHITESPACE
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password,
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses JOIN users ON users.id = addresses.user_id
WHERE users.name = ?
('jack',)
{stop}>>> jacks_addresses
[<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
>>> jacks_addresses[0].user
<User('jack','Jack Bean', 'gjffdd')>
For more information on eager loading, including how to configure various forms
of loading by default, see the section :ref:`loading_toplevel`.
Deleting
========
@@ -1502,9 +1532,13 @@ the session, then we'll issue a ``count`` query to see that no rows remain:
(None, 2)
DELETE FROM users WHERE users.id = ?
(5,)
SELECT count(1) AS count_1
FROM users
WHERE users.name = ?
SELECT count(*) AS count_1
FROM (SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.name = ?) AS anon_1
('jack',)
{stop}0
@@ -1515,9 +1549,12 @@ So far, so good. How about Jack's ``Address`` objects ?
{sql}>>> session.query(Address).filter(
... Address.email_address.in_(['jack@google.com', 'j25@yahoo.com'])
... ).count() # doctest: +NORMALIZE_WHITESPACE
SELECT count(1) AS count_1
FROM addresses
WHERE addresses.email_address IN (?, ?)
SELECT count(*) AS count_1
FROM (SELECT addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.email_address IN (?, ?)) AS anon_1
('jack@google.com', 'j25@yahoo.com')
{stop}2
@@ -1598,9 +1635,12 @@ removing an address from his ``addresses`` collection will result in that
... ).count() # doctest: +NORMALIZE_WHITESPACE
DELETE FROM addresses WHERE addresses.id = ?
(2,)
SELECT count(1) AS count_1
FROM addresses
WHERE addresses.email_address IN (?, ?)
SELECT count(*) AS count_1
FROM (SELECT addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.email_address IN (?, ?)) AS anon_1
('jack@google.com', 'j25@yahoo.com')
{stop}1
@@ -1615,18 +1655,25 @@ Deleting Jack will delete both Jack and his remaining ``Address``:
(1,)
DELETE FROM users WHERE users.id = ?
(5,)
SELECT count(1) AS count_1
FROM users
WHERE users.name = ?
SELECT count(*) AS count_1
FROM (SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.name = ?) AS anon_1
('jack',)
{stop}0
{sql}>>> session.query(Address).filter(
... Address.email_address.in_(['jack@google.com', 'j25@yahoo.com'])
... ).count() # doctest: +NORMALIZE_WHITESPACE
SELECT count(1) AS count_1
FROM addresses
WHERE addresses.email_address IN (?, ?)
SELECT count(*) AS count_1
FROM (SELECT addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.email_address IN (?, ?)) AS anon_1
('jack@google.com', 'j25@yahoo.com')
{stop}0
+1 -1
View File
@@ -2200,7 +2200,7 @@ class Engine(Connectable, log.Identified):
@property
@util.deprecated("0.7",
"Use :attr:`.expression.func` to create function constructs.")
"Use :attr:`~sqlalchemy.sql.expression.func` to create function constructs.")
def func(self):
return expression._FunctionGenerator(bind=self)
+1 -1
View File
@@ -2336,7 +2336,7 @@ class Query(object):
to count, to skip the usage of a subquery or
otherwise control of the FROM clause,
or to use other aggregate functions,
use :attr:`.func` expressions in conjunction
use :attr:`~sqlalchemy.sql.expression.func` expressions in conjunction
with :meth:`~.Session.query`, i.e.::
from sqlalchemy import func