at mapper config level using "lazy='subquery'" and at the query
options level using "subqueryload(props..)",
"subqueryload_all(props...)". [ticket:1675]
+
+ - To accomodate the fact that there are now two kinds of eager
+ loading available, the new names for eagerload() and
+ eagerload_all() are joinedload() and joinedload_all(). The
+ old names will remain as synonyms for the foreseeable future.
+
+ - The "lazy" flag on the relationship() function now accepts
+ a string argument for all kinds of loading: "select", "joined",
+ "subquery", "noload" and "dynamic", where the default is now
+ "select". The old values of True/
+ False/None still retain their usual meanings and will remain
+ as synonyms for the foreseeable future.
- Fixed bug in Query whereby calling q.join(prop).from_self(...).
join(prop) would fail to render the second join outside the
'addresses': relationship(Address, order_by=addresses_table.c.address_id)
})
-Note that when using eager loaders with relationships, 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.relationship` level. To control ordering at the query level based on a related table, you ``join()`` to that relationship, then order by it::
+Note that when using joined eager loaders with relationships, 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.relationship` level. To control ordering at the query level based on a related table, you ``join()`` to that relationship, then order by it::
session.query(User).join('addresses').order_by(Address.street)
Configuring Eager Loading
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Eager loading of relationships occurs using joins or outerjoins from parent to child table during a normal query operation, such that the parent and its child collection can be populated from a single SQL statement. SQLAlchemy's eager loading uses aliased tables in all cases when joining to related items, so it is compatible with self-referential joining. However, to use eager loading with a self-referential relationship, SQLAlchemy needs to be told how many levels deep it should join; otherwise the eager load will not take place. This depth setting is configured via ``join_depth``:
+Eager loading of relationships occurs using joins or outerjoins from parent to child table during a normal query operation, such that the parent and its child collection can be populated from a single SQL statement, or a second statement for all collections at once. SQLAlchemy's joined and subquery eager loading uses aliased tables in all cases when joining to related items, so it is compatible with self-referential joining. However, to use eager loading with a self-referential relationship, SQLAlchemy needs to be told how many levels deep it should join; otherwise the eager load will not take place. This depth setting is configured via ``join_depth``:
.. sourcecode:: python+sql
mapper(Node, nodes, properties={
- 'children': relationship(Node, lazy=False, join_depth=2)
+ 'children': relationship(Node, lazy='joined', join_depth=2)
})
{sql}session.query(Node).all()
The collections package provides additional decorators and support for authoring custom types. See the :mod:`sqlalchemy.orm.collections` package for more information and discussion of advanced usage and Python 2.3-compatible decoration options.
+.. _mapper_loader_strategies:
+
Configuring Loader Strategies: Lazy Loading, Eager Loading
-----------------------------------------------------------
+.. note:: SQLAlchemy version 0.6beta3 introduces the :func:`~sqlalchemy.orm.joinedload`, :func:`~sqlalchemy.orm.joinedload_all`, :func:`~sqlalchemy.orm.subqueryload` and :func:`~sqlalchemy.orm.subqueryload_all` functions described in this section. In previous versions, including 0.5 and 0.4, use :func:`~sqlalchemy.orm.eagerload` and :func:`~sqlalchemy.orm.eagerload_all`. Additionally, the ``lazy`` keyword argument on :func:`~sqlalchemy.orm.relationship` accepts the values ``True``, ``False`` and ``None`` in previous versions, whereas in the latest 0.6 it also accepts the arguments ``select``, ``joined``, ``noload``, and ``subquery``.
+
In the :ref:`ormtutorial_toplevel`, we introduced the concept of **Eager Loading**. We used an ``option`` in conjunction with the :class:`~sqlalchemy.orm.query.Query` object in order to indicate that a relationship should be loaded at the same time as the parent, within a single SQL query:
.. sourcecode:: python+sql
- {sql}>>> jack = session.query(User).options(eagerload('addresses')).filter_by(name='jack').all() #doctest: +NORMALIZE_WHITESPACE
+ {sql}>>> jack = session.query(User).options(joinedload('addresses')).filter_by(name='jack').all() #doctest: +NORMALIZE_WHITESPACE
SELECT addresses_1.id AS addresses_1_id, addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id, users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname, users.password AS users_password
.. sourcecode:: python+sql
{sql}>>> jack.addresses
- SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
+ SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address,
+ addresses.user_id AS addresses_user_id
FROM addresses
WHERE ? = addresses.user_id
[5]
{stop}[<Address(u'jack@google.com')>, <Address(u'j25@yahoo.com')>]
-The default **loader strategy** for any :func:`~sqlalchemy.orm.relationship` is configured by the ``lazy`` keyword argument, which defaults to ``True``. Below we set it as ``False`` so that the ``children`` relationship is eager loading:
+A second option for eager loading exists, called "subquery" loading. This kind of eager loading emits an additional SQL statement for each collection requested, aggregated across all parent objects:
+
+.. sourcecode:: python+sql
+
+ {sql}>>>jack = session.query(User).options(subqueryload('addresses')).filter_by(name='jack').all()
+ 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 = ?
+ ('jack',)
+ SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address,
+ addresses.user_id AS addresses_user_id, anon_1.users_id AS anon_1_users_id
+ FROM (SELECT users.id AS users_id
+ FROM users
+ WHERE users.name = ?) AS anon_1 JOIN addresses ON anon_1.users_id = addresses.user_id
+ ORDER BY anon_1.users_id, addresses.id
+ ('jack',)
+
+The default **loader strategy** for any :func:`~sqlalchemy.orm.relationship` is configured by the ``lazy`` keyword argument, which defaults to ``select``. Below we set it as ``joined`` so that the ``children`` relationship is eager loading, using a join:
+
+.. sourcecode:: python+sql
+
+ # load the 'children' collection using LEFT OUTER JOIN
+ mapper(Parent, parent_table, properties={
+ 'children': relationship(Child, lazy='joined')
+ })
+
+We can also set it to eagerly load using a second query for all collections, using ``subquery``:
.. sourcecode:: python+sql
- # eager load 'children' attribute
+ # load the 'children' attribute using a join to a subquery
mapper(Parent, parent_table, properties={
- 'children': relationship(Child, lazy=False)
+ 'children': relationship(Child, lazy='subquery')
})
-The loader strategy can be changed from lazy to eager as well as eager to lazy using the :func:`~sqlalchemy.orm.eagerload` and :func:`~sqlalchemy.orm.lazyload` query options:
+When querying, all three choices of loader strategy are available on a per-query basis, using the :func:`~sqlalchemy.orm.joinedload`, :func:`~sqlalchemy.orm.subqueryload` and :func:`~sqlalchemy.orm.lazyload` query options:
.. sourcecode:: python+sql
# set children to load lazily
session.query(Parent).options(lazyload('children')).all()
- # set children to load eagerly
- session.query(Parent).options(eagerload('children')).all()
+ # set children to load eagerly with a join
+ session.query(Parent).options(joinedload('children')).all()
+
+ # set children to load eagerly with a second statement
+ session.query(Parent).options(subqueryload('children')).all()
To reference a relationship that is deeper than one level, separate the names by periods:
.. sourcecode:: python+sql
- session.query(Parent).options(eagerload('foo.bar.bat')).all()
+ session.query(Parent).options(joinedload('foo.bar.bat')).all()
-When using dot-separated names with :func:`~sqlalchemy.orm.eagerload`, option applies **only** to the actual attribute named, and **not** its ancestors. For example, suppose a mapping from ``A`` to ``B`` to ``C``, where the relationships, named ``atob`` and ``btoc``, are both lazy-loading. A statement like the following:
+When using dot-separated names with :func:`~sqlalchemy.orm.joinedload` or :func:`~sqlalchemy.orm.subqueryload`, option applies **only** to the actual attribute named, and **not** its ancestors. For example, suppose a mapping from ``A`` to ``B`` to ``C``, where the relationships, named ``atob`` and ``btoc``, are both lazy-loading. A statement like the following:
.. sourcecode:: python+sql
- session.query(A).options(eagerload('atob.btoc')).all()
+ session.query(A).options(joinedload('atob.btoc')).all()
will load only ``A`` objects to start. When the ``atob`` attribute on each ``A`` is accessed, the returned ``B`` objects will *eagerly* load their ``C`` objects.
-Therefore, to modify the eager load to load both ``atob`` as well as ``btoc``, place eagerloads for both:
+Therefore, to modify the eager load to load both ``atob`` as well as ``btoc``, place joinedloads for both:
.. sourcecode:: python+sql
- session.query(A).options(eagerload('atob'), eagerload('atob.btoc')).all()
+ session.query(A).options(joinedload('atob'), joinedload('atob.btoc')).all()
-or more simply just use :func:`~sqlalchemy.orm.eagerload_all`:
+or more simply just use :func:`~sqlalchemy.orm.joinedload_all` or :func:`~sqlalchemy.orm.subqueryload_all`:
.. sourcecode:: python+sql
- session.query(A).options(eagerload_all('atob.btoc')).all()
+ session.query(A).options(joinedload_all('atob.btoc')).all()
There are two other loader strategies available, **dynamic loading** and **no loading**; these are described in :ref:`largecollections`.
+What Kind of Loading to Use ?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Which type of loading to use typically comes down to optimizing the tradeoff between number of SQL executions, complexity of SQL emitted, and amount of data fetched. Lets take two examples, a :func:`~sqlalchemy.orm.relationship` which references a collection, and a :func:`~sqlalchemy.orm.relationship` that references a scalar many-to-one reference.
+
+* One to Many Collection
+
+ * When using the default lazy loading, if you load 100 objects, and then access a collection on each of
+ them, a total of 101 SQL statements will be emitted, although each statement will typically be a
+ simple SELECT without any joins.
+
+ * When using joined loading, the load of 100 objects and their collections will emit only one SQL
+ statement. However, the
+ total number of rows fetched will be equal to the sum of the size of all the collections, plus one
+ extra row for each parent object that has an empty collection. Each row will also contain the full
+ set of columns represented by the parents, repeated for each collection item - SQLAlchemy does not
+ re-fetch these columns other than those of the primary key, however most DBAPIs (with some
+ exceptions) will transmit the full data of each parent over the wire to the client connection in
+ any case. Therefore joined eager loading only makes sense when the size of the collections are
+ relatively small. The LEFT OUTER JOIN can also be performance intensive compared to an INNER join.
+
+ * When using subquery loading, the load of 100 objects will emit two SQL statements. The second
+ statement will fetch a total number of rows equal to the sum of the size of all collections. An
+ INNER JOIN is used, and a minimum of parent columns are requested, only the primary keys. So a
+ subquery load makes sense when the collections are larger.
+
+ * When multiple levels of depth are used with joined or subquery loading, loading collections-within-
+ collections will multiply the total number of rows fetched in a cartesian fashion. Both forms
+ of eager loading always join from the original parent class.
+
+* Many to One Reference
+
+ * When using the default lazy loading, a load of 100 objects will like in the case of the collection
+ emit as many as 101 SQL statements. However - there is a significant exception to this, in that
+ if the many-to-one reference is a simple foreign key reference to the target's primary key, each
+ reference will be checked first in the current identity map using ``query.get()``. So here,
+ if the collection of objects references a relatively small set of target objects, or the full set
+ of possible target objects have already been loaded into the session and are strongly referenced,
+ using the default of `lazy='select'` is by far the most efficient way to go.
+
+ * When using joined loading, the load of 100 objects will emit only one SQL statement. The join
+ will be a LEFT OUTER JOIN, and the total number of rows will be equal to 100 in all cases.
+ If you know that each parent definitely has a child (i.e. the foreign
+ key reference is NOT NULL), the joined load can be configured with ``innerjoin=True``, which is
+ usually specified within the :func:`~sqlalchemy.orm.relationship`. For a load of objects where
+ there are many possible target references which may have not been loaded already, joined loading
+ with an INNER JOIN is extremely efficient.
+
+ * Subquery loading will issue a second load for all the child objects, so for a load of 100 objects
+ there would be two SQL statements emitted. There's probably not much advantage here over
+ joined loading, however, except perhaps that subquery loading can use an INNER JOIN in all cases
+ whereas joined loading requires that the foreign key is NOT NULL.
+
Routing Explicit Joins/Statements into Eagerly Loaded Collections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The behavior of :func:`~sqlalchemy.orm.eagerload()` is such that joins are created automatically, the results of which are routed into collections and scalar references on loaded objects. It is often the case that a query already includes the necessary joins which represent a particular collection or scalar reference, and the joins added by the eagerload feature are redundant - yet you'd still like the collections/references to be populated.
+The behavior of :func:`~sqlalchemy.orm.joinedload()` is such that joins are created automatically, the results of which are routed into collections and scalar references on loaded objects. It is often the case that a query already includes the necessary joins which represent a particular collection or scalar reference, and the joins added by the joinedload feature are redundant - yet you'd still like the collections/references to be populated.
-For this SQLAlchemy supplies the :func:`~sqlalchemy.orm.contains_eager()` option. This option is used in the same manner as the :func:`~sqlalchemy.orm.eagerload()` option except it is assumed that the :class:`~sqlalchemy.orm.query.Query` will specify the appropriate joins explicitly. Below it's used with a ``from_statement`` load::
+For this SQLAlchemy supplies the :func:`~sqlalchemy.orm.contains_eager()` option. This option is used in the same manner as the :func:`~sqlalchemy.orm.joinedload()` option except it is assumed that the :class:`~sqlalchemy.orm.query.Query` will specify the appropriate joins explicitly. Below it's used with a ``from_statement`` load::
# mapping is the users->addresses mapping
mapper(User, users_table, properties={
When we accessed the ``addresses`` collection, SQL was suddenly issued. This is an example of a **lazy loading relationship**. The ``addresses`` collection is now loaded and behaves just like an ordinary list.
-If you want to reduce the number of queries (dramatically, in many cases), we can apply an **eager load** to the query operation, using the :func:`~sqlalchemy.orm.eagerload` function. This function is a **query option** that gives additional instructions to the query on how we would like it to load, in this case we'd like to indicate that we'd like ``addresses`` to load "eagerly". SQLAlchemy then constructs an outer join between the ``users`` and ``addresses`` tables, and loads them at once, populating the ``addresses`` collection on each ``User`` object if it's not already populated:
+If you want to reduce the number of queries (dramatically, in many cases), we can apply an **eager load** to the query operation, using the :func:`~sqlalchemy.orm.joinedload` function. This function is a **query option** that gives additional instructions to the query on how we would like it to load, in this case we'd like to indicate that we'd like ``addresses`` to load "eagerly". SQLAlchemy then constructs an outer join between the ``users`` and ``addresses`` tables, and loads them at once, populating the ``addresses`` collection on each ``User`` object if it's not already populated:
.. sourcecode:: python+sql
- >>> from sqlalchemy.orm import eagerload
+ >>> from sqlalchemy.orm import joinedload
{sql}>>> jack = session.query(User).\
- ... options(eagerload('addresses')).\
+ ... options(joinedload('addresses')).\
... filter_by(name='jack').one() #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_1.id AS addresses_1_id, addresses_1.email_address
>>> jack.addresses
[<Address('jack@google.com')>, <Address('j25@yahoo.com')>]
-See :func:`~sqlalchemy.orm.eagerload` for further detail. We'll also see another way to "eagerly" load in the next section.
+See :ref:`mapper_loader_strategies` for information on :func:`~sqlalchemy.orm.joinedload` and its new brother, :func:`~sqlalchemy.orm.subqueryload`. We'll also see another way to "eagerly" load in the next section.
Querying with Joins
====================
-While :func:`~sqlalchemy.orm.eagerload` created a JOIN specifically to populate a collection, we can also work explicitly with joins in many ways. For example, to construct a simple inner join between ``User`` and ``Address``, we can just :meth:`~sqlalchemy.orm.query.Query.filter()` their related columns together. Below we load the ``User`` and ``Address`` entities at once using this method:
+While :func:`~sqlalchemy.orm.joinedload` created a JOIN specifically to populate a collection, we can also work explicitly with joins in many ways. For example, to construct a simple inner join between ``User`` and ``Address``, we can just :meth:`~sqlalchemy.orm.query.Query.filter()` their related columns together. Below we load the ``User`` and ``Address`` entities at once using this method:
.. sourcecode:: python+sql
Using join() to Eagerly Load Collections/Attributes
-------------------------------------------------------
-The "eager loading" capabilities of the :func:`~sqlalchemy.orm.eagerload` function and the join-construction capabilities of :meth:`~sqlalchemy.orm.query.Query.join()` or an equivalent can be combined together using the :func:`~sqlalchemy.orm.contains_eager` option. This is typically used
+The "eager loading" capabilities of the :func:`~sqlalchemy.orm.joinedload` function and the join-construction capabilities of :meth:`~sqlalchemy.orm.query.Query.join()` or an equivalent can be combined together using the :func:`~sqlalchemy.orm.contains_eager` option. This is typically used
for a query that is already joining to some related entity (more often than not via many-to-one), and you'd like the related entity to also be loaded onto the resulting objects
in one step without the need for additional queries and without the "automatic" join embedded
-by the :func:`~sqlalchemy.orm.eagerload` function:
+by the :func:`~sqlalchemy.orm.joinedload` function:
.. sourcecode:: python+sql
.. autofunction:: extension
+.. autofunction:: joinedload
+
+.. autofunction:: joinedload_all
+
.. autofunction:: lazyload
+.. autofunction:: subqueryload
+
+.. autofunction:: subqueryload_all
+
.. autofunction:: undefer
'eagerload_all',
'extension',
'join',
+ 'joinedload',
+ 'joinedload_all',
'lazyload',
'mapper',
'make_transient',
eager loads will automatically stop chaining joins when they
encounter a mapper which is already higher up in the chain.
- :param lazy=(True|False|None|'dynamic'):
+ :param lazy=('select'|'joined'|'subquery'|'noload'|'dynamic'):
specifies how the related items should be loaded. Values include:
- True - items should be loaded lazily when the property is first
+ 'select' - items should be loaded lazily when the property is first
accessed.
- False - items should be loaded "eagerly" in the same query as
+ 'joined' - items should be loaded "eagerly" in the same query as
that of the parent, using a JOIN or LEFT OUTER JOIN.
+
+ 'subquery' - items should be loaded "eagerly" within the same
+ query as that of the parent, using a second SQL statement
+ which issues a JOIN to a subquery of the original
+ statement.
- None - no loading should occur at any time. This is to support
+ 'noload' - no loading should occur at any time. This is to support
"write-only" attributes, or attributes which are
populated in some manner specific to the application.
``remove()`` for write operations; changes to the
dynamic property will not be visible until the data
is flushed to the database.
-
+
+ True - a synonym for 'select'
+
+ False - a synonyn for 'joined'
+
+ None - a synonym for 'noload'
+
:param order_by:
indicates the ordering that should be applied when loading these
items.
return ExtensionOption(ext)
@sa_util.accepts_a_list_as_starargs(list_deprecation='deprecated')
-def eagerload(*keys, **kw):
+def joinedload(*keys, **kw):
"""Return a ``MapperOption`` that will convert the property of the given
- name into an eager load.
+ name into an joined eager load.
+
+ .. note:: This function is known as :func:`eagerload` in all versions
+ of SQLAlchemy prior to version 0.6beta3, including the 0.5 and 0.4 series.
+ :func:`eagerload` will remain available for
+ the foreseeable future in order to enable cross-compatibility.
Used with :meth:`~sqlalchemy.orm.query.Query.options`.
examples::
- # eagerload the "orders" colleciton on "User"
- query(User).options(eagerload(User.orders))
+ # joined-load the "orders" colleciton on "User"
+ query(User).options(joinedload(User.orders))
- # eagerload the "keywords" collection on each "Item",
+ # joined-load the "keywords" collection on each "Item",
# but not the "items" collection on "Order" - those
# remain lazily loaded.
- query(Order).options(eagerload(Order.items, Item.keywords))
+ query(Order).options(joinedload(Order.items, Item.keywords))
- # to eagerload across both, use eagerload_all()
- query(Order).options(eagerload_all(Order.items, Item.keywords))
+ # to joined-load across both, use joinedload_all()
+ query(Order).options(joinedload_all(Order.items, Item.keywords))
- :func:`eagerload` also accepts a keyword argument `innerjoin=True` which
+ :func:`joinedload` also accepts a keyword argument `innerjoin=True` which
indicates using an inner join instead of an outer::
- query(Order).options(eagerload(Order.user, innerjoin=True))
+ query(Order).options(joinedload(Order.user, innerjoin=True))
- Note that the join created by :func:`eagerload` is aliased such that
- no other aspects of the query will affect what it loads. To use eager
+ Note that the join created by :func:`joinedload` is aliased such that
+ no other aspects of the query will affect what it loads. To use joined eager
loading with a join that is constructed manually using :meth:`~sqlalchemy.orm.query.Query.join`
or :func:`~sqlalchemy.orm.join`, see :func:`contains_eager`.
+ See also: :func:`subqueryload`, :func:`lazyload`
+
"""
innerjoin = kw.pop('innerjoin', None)
if innerjoin is not None:
return strategies.EagerLazyOption(keys, lazy=False)
@sa_util.accepts_a_list_as_starargs(list_deprecation='deprecated')
-def eagerload_all(*keys, **kw):
+def joinedload_all(*keys, **kw):
"""Return a ``MapperOption`` that will convert all properties along the
- given dot-separated path into an eager load.
+ given dot-separated path into an joined eager load.
+
+ .. note:: This function is known as :func:`eagerload_all` in all versions
+ of SQLAlchemy prior to version 0.6beta3, including the 0.5 and 0.4 series.
+ :func:`eagerload_all` will remain available for
+ the foreseeable future in order to enable cross-compatibility.
Used with :meth:`~sqlalchemy.orm.query.Query.options`.
For example::
- query.options(eagerload_all('orders.items.keywords'))...
+ query.options(joinedload_all('orders.items.keywords'))...
will set all of 'orders', 'orders.items', and 'orders.items.keywords' to
- load in one eager load.
+ load in one joined eager load.
Individual descriptors are accepted as arguments as well::
- query.options(eagerload_all(User.orders, Order.items, Item.keywords))
+ query.options(joinedload_all(User.orders, Order.items, Item.keywords))
The keyword arguments accept a flag `innerjoin=True|False` which will
override the value of the `innerjoin` flag specified on the relationship().
+ See also: :func:`subqueryload_all`, :func:`lazyload`
+
"""
innerjoin = kw.pop('innerjoin', None)
if innerjoin is not None:
else:
return strategies.EagerLazyOption(keys, lazy=False, chained=True)
+def eagerload(*args, **kwargs):
+ """A synonym for :func:`joinedload()`."""
+ return joinedload(*args, **kwargs)
+
+def eagerload_all(*args, **kwargs):
+ """A synonym for :func:`joinedload_all()`"""
+ return joinedload_all(*args, **kwargs)
+
def subqueryload(*keys):
- return strategies.EagerLazyOption(keys, _strategy_cls=strategies.SubqueryLoader)
+ """Return a ``MapperOption`` that will convert the property
+ of the given name into an subquery eager load.
+
+ .. note:: This function is new as of SQLAlchemy version 0.6beta3.
+
+ Used with :meth:`~sqlalchemy.orm.query.Query.options`.
+
+ examples::
+
+ # subquery-load the "orders" colleciton on "User"
+ query(User).options(subqueryload(User.orders))
+
+ # subquery-load the "keywords" collection on each "Item",
+ # but not the "items" collection on "Order" - those
+ # remain lazily loaded.
+ query(Order).options(subqueryload(Order.items, Item.keywords))
+
+ # to subquery-load across both, use subqueryload_all()
+ query(Order).options(subqueryload_all(Order.items, Item.keywords))
+
+ See also: :func:`joinedload`, :func:`lazyload`
+
+ """
+ return strategies.EagerLazyOption(keys, lazy="subquery")
def subqueryload_all(*keys):
- return strategies.EagerLazyOption(keys, _strategy_cls=strategies.SubqueryLoader, chained=True)
+ """Return a ``MapperOption`` that will convert all properties along the
+ given dot-separated path into a subquery eager load.
+
+ .. note:: This function is new as of SQLAlchemy version 0.6beta3.
+
+ Used with :meth:`~sqlalchemy.orm.query.Query.options`.
+
+ For example::
+
+ query.options(subqueryload_all('orders.items.keywords'))...
+
+ will set all of 'orders', 'orders.items', and 'orders.items.keywords' to
+ load in one subquery eager load.
+
+ Individual descriptors are accepted as arguments as well::
+
+ query.options(subquryload_all(User.orders, Order.items, Item.keywords))
+
+ See also: :func:`joinedload_all`, :func:`lazyload`
+
+ """
+ return strategies.EagerLazyOption(keys, lazy="subquery", chained=True)
@sa_util.accepts_a_list_as_starargs(list_deprecation='deprecated')
def lazyload(*keys):
Used with :meth:`~sqlalchemy.orm.query.Query.options`.
+ See also: :func:`eagerload`, :func:`subqueryload`
+
"""
return strategies.EagerLazyOption(keys, lazy=True)
Used with :meth:`~sqlalchemy.orm.query.Query.options`.
+ See also: :func:`lazyload`, :func:`eagerload`, :func:`subqueryload`
+
"""
return strategies.EagerLazyOption(keys, lazy=None)
self.comparator_factory = comparator_factory or RelationshipProperty.Comparator
self.comparator = self.comparator_factory(self, None)
util.set_creation_order(self)
-
+
if strategy_class:
self.strategy_class = strategy_class
- elif self.lazy == 'dynamic':
+ elif self.lazy== 'dynamic':
from sqlalchemy.orm import dynamic
self.strategy_class = dynamic.DynaLoader
- elif self.lazy is False or self.lazy == 'joined':
- self.strategy_class = strategies.EagerLoader
- elif self.lazy is None or self.lazy == 'noload':
- self.strategy_class = strategies.NoLoader
- elif self.lazy is False or self.lazy == 'select':
- self.strategy_class = strategies.LazyLoader
- elif self.lazy == 'subquery':
- self.strategy_class = strategies.SubqueryLoader
else:
- self.strategy_class = strategies.LazyLoader
+ self.strategy_class = strategies.factory(self.lazy)
self._reverse_property = set()
class EagerLazyOption(StrategizedOption):
def __init__(self, key, lazy=True, chained=False,
- propagate_to_loaders=True,
- _strategy_cls=None
+ propagate_to_loaders=True
):
super(EagerLazyOption, self).__init__(key)
self.lazy = lazy
self.chained = chained
self.propagate_to_loaders = propagate_to_loaders
- self.strategy_cls = _strategy_cls
+ self.strategy_cls = factory(lazy)
def is_chained(self):
return not self.lazy and self.chained
def get_strategy_class(self):
- if self.strategy_cls:
- return self.strategy_cls
- elif self.lazy:
- return LazyLoader
- elif self.lazy is False:
- return EagerLoader
- elif self.lazy is None:
- return NoLoader
-
+ return self.strategy_cls
+
+def factory(identifier):
+ if identifier is False or identifier == 'joined':
+ return EagerLoader
+ elif identifier is None or identifier == 'noload':
+ return NoLoader
+ elif identifier is False or identifier == 'select':
+ return LazyLoader
+ elif identifier == 'subquery':
+ return SubqueryLoader
+ else:
+ return LazyLoader
+
+
+
class EagerJoinOption(PropertyOption):
def __init__(self, key, innerjoin, chained=False):
from sqlalchemy.test import testing
from sqlalchemy import MetaData, Integer, String, ForeignKey, ForeignKeyConstraint, asc, Index
from sqlalchemy.test.schema import Table, Column
-from sqlalchemy.orm import relationship, create_session, class_mapper, eagerload, compile_mappers, backref, clear_mappers, polymorphic_union, deferred
+from sqlalchemy.orm import relationship, create_session, class_mapper, joinedload, compile_mappers, backref, clear_mappers, polymorphic_union, deferred
from sqlalchemy.test.testing import eq_
from sqlalchemy.util import classproperty
sess.add(u1)
sess.flush()
sess.expunge_all()
- eq_(sess.query(User).options(eagerload(User.addresses)).all(), [User(name='u1', addresses=[
+ eq_(sess.query(User).options(joinedload(User.addresses)).all(), [User(name='u1', addresses=[
Address(email='one'),
Address(email='two'),
])])
from sqlalchemy import MetaData, Integer, String, ForeignKey, select, desc, func, util
from sqlalchemy.test.schema import Table
from sqlalchemy.test.schema import Column
-from sqlalchemy.orm import relationship, sessionmaker, scoped_session, class_mapper, mapper, eagerload, compile_mappers, aliased
+from sqlalchemy.orm import relationship, sessionmaker, scoped_session, class_mapper, mapper, joinedload, compile_mappers, aliased
from sqlalchemy.test.testing import eq_
from test.orm._base import ComparableEntity, MappedTest
)
def test_query(self):
- q = Session.query(User).filter(User.name=='ed').options(eagerload(User.addresses))
+ q = Session.query(User).filter(User.name=='ed').options(joinedload(User.addresses))
eq_(q.all(), [User(name='ed', addresses=[Address(id=2), Address(id=3), Address(id=4)])])
q2 = serializer.loads(serializer.dumps(q, -1), users.metadata, Session)
eq_(node.children[0], B(id=2, name='b2',b_data='l'))
sess.expunge_all()
- node = sess.query(B).options(eagerload(B.children)).filter(B.id==bid).all()[0]
+ node = sess.query(B).options(joinedload(B.children)).filter(B.id==bid).all()[0]
eq_(node, B(id=1, name='b1',b_data='i'))
eq_(node.children[0], B(id=2, name='b2',b_data='l'))
self.assert_sql_count(testing.db, go, 2)
session.expunge_all()
def go():
- c2 = session.query(Company).options(eagerload(Company.employees)).get(c.id)
+ c2 = session.query(Company).options(joinedload(Company.employees)).get(c.id)
assert set([repr(x) for x in c2.employees]) == set(["Engineer Kurt knows how to hack", "Manager Tom knows how to manage things"])
self.assert_sql_count(testing.db, go, 1)
def go():
eq_(
[C(many_a=[A(aname='a1'), B(bname='b1'), B(bname='b2')]), C(many_a=[A(aname='a2')])],
- sess.query(C).options(eagerload(C.many_a)).order_by(C.id).all(),
+ sess.query(C).options(joinedload(C.many_a)).order_by(C.id).all(),
)
self.assert_sql_count(testing.db, go, 1)
session.expunge_all()
def go():
- testcar = session.query(Car).options(eagerload('employee')).get(car1.car_id)
+ testcar = session.query(Car).options(joinedload('employee')).get(car1.car_id)
assert str(testcar.employee) == "Engineer E4, status X"
self.assert_sql_count(testing.db, go, 1)
# and now for the lightning round, eager !
def go():
- testcar = session.query(Car).options(eagerload('employee')).get(car1.car_id)
+ testcar = session.query(Car).options(joinedload('employee')).get(car1.car_id)
assert str(testcar.employee) == "Engineer E4, status X"
self.assert_sql_count(testing.db, go, 1)
def test_primary_eager_aliasing(self):
sess = create_session()
- # for both eagerload() and subqueryload(), if the original q is not loading
- # the subclass table, the eagerload doesn't happen.
+ # for both joinedload() and subqueryload(), if the original q is not loading
+ # the subclass table, the joinedload doesn't happen.
def go():
- eq_(sess.query(Person).options(eagerload(Engineer.machines))[1:3], all_employees[1:3])
+ eq_(sess.query(Person).options(joinedload(Engineer.machines))[1:3], all_employees[1:3])
self.assert_sql_count(testing.db, go, {'':6, 'Polymorphic':3}.get(select_type, 4))
sess = create_session()
sess = create_session()
# assert the JOINs dont over JOIN
- assert sess.query(Person).with_polymorphic('*').options(eagerload(Engineer.machines)).\
+ assert sess.query(Person).with_polymorphic('*').options(joinedload(Engineer.machines)).\
limit(2).offset(1).with_labels().subquery().count().scalar() == 2
def go():
eq_(
sess.query(Person).with_polymorphic('*').
- options(eagerload(Engineer.machines))[1:3],
+ options(joinedload(Engineer.machines))[1:3],
all_employees[1:3])
self.assert_sql_count(testing.db, go, 3)
sess = create_session()
def go():
- # currently, it doesn't matter if we say Company.employees, or Company.employees.of_type(Engineer). eagerloader doesn't
+ # currently, it doesn't matter if we say Company.employees, or Company.employees.of_type(Engineer). joinedloader doesn't
# pick up on the "of_type()" as of yet.
eq_(
sess.query(Company).options(
- eagerload_all(Company.employees.of_type(Engineer), Engineer.machines
+ joinedload_all(Company.employees.of_type(Engineer), Engineer.machines
)).all(),
assert_result)
- # in the case of select_type='', the eagerload doesn't take in this case;
- # it eagerloads company->people, then a load for each of 5 rows, then lazyload of "machines"
+ # in the case of select_type='', the joinedload doesn't take in this case;
+ # it joinedloads company->people, then a load for each of 5 rows, then lazyload of "machines"
self.assert_sql_count(testing.db, go, {'':7, 'Polymorphic':1}.get(select_type, 2))
sess = create_session()
self.assert_sql_count(testing.db, go, {'':9, 'Joins':6,'Unions':6,'Polymorphic':5,'AliasedJoins':6}[select_type])
- def test_eagerload_on_subclass(self):
+ def test_joinedload_on_subclass(self):
sess = create_session()
def go():
- # test load People with eagerload to engineers + machines
- eq_(sess.query(Person).with_polymorphic('*').options(eagerload(Engineer.machines)).filter(Person.name=='dilbert').all(),
+ # test load People with joinedload to engineers + machines
+ eq_(sess.query(Person).with_polymorphic('*').options(joinedload(Engineer.machines)).filter(Person.name=='dilbert').all(),
[Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", machines=[Machine(name="IBM ThinkPad"), Machine(name="IPhone")])]
)
self.assert_sql_count(testing.db, go, 1)
session.add(c1)
session.flush()
- q = session.query(Child1).options(eagerload('left_child2'))
+ q = session.query(Child1).options(joinedload('left_child2'))
# test that the splicing of the join works here, doesnt break in the middle of "parent join child1"
self.assert_compile(q.limit(1).with_labels().statement,
assert row.left_child2
class EagerToSubclassTest(_base.MappedTest):
- """Test eagerloads to subclass mappers"""
+ """Test joinedloads to subclass mappers"""
run_setup_classes = 'once'
run_setup_mappers = 'once'
sess.flush()
@testing.resolve_artifact_names
- def test_eagerload(self):
+ def test_joinedload(self):
sess = create_session()
def go():
eq_(
- sess.query(Parent).options(eagerload(Parent.children)).all(),
+ sess.query(Parent).options(joinedload(Parent.children)).all(),
[
Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]),
Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')])
self.assert_sql_count(testing.db, go, 1)
class SubClassEagerToSubClassTest(_base.MappedTest):
- """Test eagerloads from subclass to subclass mappers"""
+ """Test joinedloads from subclass to subclass mappers"""
run_setup_classes = 'once'
run_setup_mappers = 'once'
sess.flush()
@testing.resolve_artifact_names
- def test_eagerload(self):
+ def test_joinedload(self):
sess = create_session()
def go():
eq_(
- sess.query(Subparent).options(eagerload(Subparent.children)).all(),
+ sess.query(Subparent).options(joinedload(Subparent.children)).all(),
[
Subparent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]),
Subparent(data='p2', children=[Sub(data='s4'), Sub(data='s5')])
sess.expunge_all()
def go():
eq_(
- sess.query(Subparent).options(eagerload("children")).all(),
+ sess.query(Subparent).options(joinedload("children")).all(),
[
Subparent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]),
Subparent(data='p2', children=[Sub(data='s4'), Sub(data='s5')])
# eager load join should limit to only "Engineer"
sess.expunge_all()
- eq_(sess.query(Company).options(eagerload('engineers')).order_by(Company.name).all(),
+ eq_(sess.query(Company).options(joinedload('engineers')).order_by(Company.name).all(),
[
Company(name='c1', engineers=[JuniorEngineer(name='Ed')]),
Company(name='c2', engineers=[Engineer(name='Kurt')])
eq_(result, [(1, u'Some Category'), (3, u'Some Category')])
@testing.resolve_artifact_names
- def test_withouteagerload(self):
+ def test_withoutjoinedload(self):
s = create_session()
l = (s.query(Thing).
select_from(tests.outerjoin(options,
eq_(result, [u'1 Some Category', u'3 Some Category'])
@testing.resolve_artifact_names
- def test_witheagerload(self):
+ def test_withjoinedload(self):
"""
- Test that an eagerload locates the correct "from" clause with which to
+ Test that an joinedload locates the correct "from" clause with which to
attach to, when presented with a query that already has a complicated
from clause.
"""
s = create_session()
- q=s.query(Thing).options(sa.orm.eagerload('category'))
+ q=s.query(Thing).options(sa.orm.joinedload('category'))
l=(q.select_from(tests.outerjoin(options,
sa.and_(tests.c.id ==
@testing.resolve_artifact_names
def test_dslish(self):
- """test the same as witheagerload except using generative"""
+ """test the same as withjoinedload except using generative"""
s = create_session()
- q = s.query(Thing).options(sa.orm.eagerload('category'))
+ q = s.query(Thing).options(sa.orm.joinedload('category'))
l = q.filter (
sa.and_(tests.c.owner_id == 1,
sa.or_(options.c.someoption == None,
@testing.resolve_artifact_names
def test_without_outerjoin_literal(self):
s = create_session()
- q = s.query(Thing).options(sa.orm.eagerload('category'))
+ q = s.query(Thing).options(sa.orm.joinedload('category'))
l = (q.filter(
(tests.c.owner_id==1) &
('options.someoption is null or options.someoption=%s' % false)).
@testing.resolve_artifact_names
def test_withoutouterjoin(self):
s = create_session()
- q = s.query(Thing).options(sa.orm.eagerload('category'))
+ q = s.query(Thing).options(sa.orm.joinedload('category'))
l = q.filter(
(tests.c.owner_id==1) &
((options.c.someoption==None) | (options.c.someoption==False))
# now query for Data objects using that above select, adding the
# "order by max desc" separately
q = (session.query(Data).
- options(sa.orm.eagerload('foo')).
+ options(sa.orm.joinedload('foo')).
select_from(datas.join(arb_data, arb_data.c.data_id == datas.c.id)).
order_by(sa.desc(arb_data.c.max)).
limit(10))
@testing.fails_on('maxdb', 'FIXME: unknown')
@testing.resolve_artifact_names
- def test_eagerload_on_path(self):
+ def test_joinedload_on_path(self):
session = create_session()
tx1 = Transaction(name='tx1')
# "accounts" off the immediate "entries"; only the "accounts" off
# the entries->transaction->entries
acc = (session.query(Account).
- options(sa.orm.eagerload_all('entries.transaction.entries.account')).
+ options(sa.orm.joinedload_all('entries.transaction.entries.account')).
order_by(Account.account_id)).first()
# no sql occurs
from sqlalchemy.test.testing import eq_, is_, is_not_
import sqlalchemy as sa
from sqlalchemy.test import testing
-from sqlalchemy.orm import eagerload, deferred, undefer, eagerload_all, backref
+from sqlalchemy.orm import joinedload, deferred, undefer, joinedload_all, backref
from sqlalchemy import Integer, String, Date, ForeignKey, and_, select, func
from sqlalchemy.test.schema import Table, Column
from sqlalchemy.orm import mapper, relationship, create_session, lazyload, aliased
def go():
eq_(
[User(id=7, addresses=[Address(id=1, email_address='jack@bean.com')])],
- sess.query(User).options(eagerload('addresses')).filter(User.id==7).all()
+ sess.query(User).options(joinedload('addresses')).filter(User.id==7).all()
)
self.assert_sql_count(testing.db, go, 1)
for opt, count in [
((
- eagerload(User.orders, Order.items),
+ joinedload(User.orders, Order.items),
), 10),
- ((eagerload("orders.items"), ), 10),
+ ((joinedload("orders.items"), ), 10),
((
- eagerload(User.orders, ),
- eagerload(User.orders, Order.items),
- eagerload(User.orders, Order.items, Item.keywords),
+ joinedload(User.orders, ),
+ joinedload(User.orders, Order.items),
+ joinedload(User.orders, Order.items, Item.keywords),
), 1),
((
- eagerload(User.orders, Order.items, Item.keywords),
+ joinedload(User.orders, Order.items, Item.keywords),
), 10),
((
- eagerload(User.orders, Order.items),
- eagerload(User.orders, Order.items, Item.keywords),
+ joinedload(User.orders, Order.items),
+ joinedload(User.orders, Order.items, Item.keywords),
), 5),
]:
sess = create_session()
def go():
eq_(self.static.item_keyword_result[0:2],
- (q.options(eagerload('keywords')).
+ (q.options(joinedload('keywords')).
join('keywords').filter(keywords.c.name == 'red')).order_by(Item.id).all())
self.assert_sql_count(testing.db, go, 1)
sess = create_session()
self.assert_compile(
- sess.query(User).options(eagerload(User.orders)).limit(10),
+ sess.query(User).options(joinedload(User.orders)).limit(10),
"SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name AS anon_1_users_name, "
"orders_1.id AS orders_1_id, orders_1.user_id AS orders_1_user_id, orders_1.address_id AS "
"orders_1_address_id, orders_1.description AS orders_1_description, orders_1.isopen AS orders_1_isopen "
)
self.assert_compile(
- sess.query(Order).options(eagerload(Order.user)).limit(10),
+ sess.query(Order).options(joinedload(Order.user)).limit(10),
"SELECT orders.id AS orders_id, orders.user_id AS orders_user_id, orders.address_id AS "
"orders_address_id, orders.description AS orders_description, orders.isopen AS orders_isopen, "
"users_1.id AS users_1_id, users_1.name AS users_1_name FROM orders LEFT OUTER JOIN users AS "
)
self.assert_compile(
- sess.query(Order).options(eagerload(Order.user, innerjoin=True)).limit(10),
+ sess.query(Order).options(joinedload(Order.user, innerjoin=True)).limit(10),
"SELECT orders.id AS orders_id, orders.user_id AS orders_user_id, orders.address_id AS "
"orders_address_id, orders.description AS orders_description, orders.isopen AS orders_isopen, "
"users_1.id AS users_1_id, users_1.name AS users_1_name FROM orders JOIN users AS "
)
self.assert_compile(
- sess.query(User).options(eagerload_all("orders.address")).limit(10),
+ sess.query(User).options(joinedload_all("orders.address")).limit(10),
"SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name AS anon_1_users_name, "
"addresses_1.id AS addresses_1_id, addresses_1.user_id AS addresses_1_user_id, "
"addresses_1.email_address AS addresses_1_email_address, orders_1.id AS orders_1_id, "
)
self.assert_compile(
- sess.query(User).options(eagerload_all("orders.items"), eagerload("orders.address")),
+ sess.query(User).options(joinedload_all("orders.items"), joinedload("orders.address")),
"SELECT users.id AS users_id, users.name AS users_name, items_1.id AS items_1_id, "
"items_1.description AS items_1_description, addresses_1.id AS addresses_1_id, "
"addresses_1.user_id AS addresses_1_user_id, addresses_1.email_address AS "
)
self.assert_compile(
- sess.query(User).options(eagerload("orders"), eagerload("orders.address", innerjoin=True)).limit(10),
+ sess.query(User).options(joinedload("orders"), joinedload("orders.address", innerjoin=True)).limit(10),
"SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name AS anon_1_users_name, "
"addresses_1.id AS addresses_1_id, addresses_1.user_id AS addresses_1_user_id, "
"addresses_1.email_address AS addresses_1_email_address, orders_1.id AS orders_1_id, "
mapper(Order, orders)
s = create_session()
assert_raises(sa.exc.SAWarning,
- s.query(User).options(eagerload(User.order)).all)
+ s.query(User).options(joinedload(User.order)).all)
@testing.resolve_artifact_names
def test_wide(self):
))
mapper(Item, items)
sess = create_session()
- self.assert_compile(sess.query(User).options(eagerload(User.orders, innerjoin=True)),
+ self.assert_compile(sess.query(User).options(joinedload(User.orders, innerjoin=True)),
"SELECT users.id AS users_id, users.name AS users_name, orders_1.id AS orders_1_id, "
"orders_1.user_id AS orders_1_user_id, orders_1.address_id AS orders_1_address_id, "
"orders_1.description AS orders_1_description, orders_1.isopen AS orders_1_isopen "
"FROM users JOIN orders AS orders_1 ON users.id = orders_1.user_id ORDER BY orders_1.id"
, use_default_dialect=True)
- self.assert_compile(sess.query(User).options(eagerload_all(User.orders, Order.items, innerjoin=True)),
+ self.assert_compile(sess.query(User).options(joinedload_all(User.orders, Order.items, innerjoin=True)),
"SELECT users.id AS users_id, users.name AS users_name, items_1.id AS items_1_id, "
"items_1.description AS items_1_description, orders_1.id AS orders_1_id, "
"orders_1.user_id AS orders_1_user_id, orders_1.address_id AS orders_1_address_id, "
def go():
eq_(
sess.query(User).options(
- eagerload(User.orders, innerjoin=True),
- eagerload(User.orders, Order.items, innerjoin=True)).
+ joinedload(User.orders, innerjoin=True),
+ joinedload(User.orders, Order.items, innerjoin=True)).
order_by(User.id).all(),
[User(id=7,
# test that default innerjoin setting is used for options
self.assert_compile(
- sess.query(Order).options(eagerload(Order.user)).filter(Order.description == 'foo'),
+ sess.query(Order).options(joinedload(Order.user)).filter(Order.description == 'foo'),
"SELECT orders.id AS orders_id, orders.user_id AS orders_user_id, orders.address_id AS "
"orders_address_id, orders.description AS orders_description, orders.isopen AS "
"orders_isopen, users_1.id AS users_1_id, users_1.name AS users_1_name "
oalias = sa.orm.aliased(Order)
def go():
- ret = sess.query(User, oalias).options(eagerload('addresses')).join(
+ ret = sess.query(User, oalias).options(joinedload('addresses')).join(
('orders', oalias)).order_by(User.id, oalias.id).all()
eq_(ret, self._assert_result())
self.assert_sql_count(testing.db, go, 6)
sess.expunge_all()
def go():
- ret = sess.query(User, oalias).options(eagerload('addresses'),
- eagerload(oalias.items)).join(
+ ret = sess.query(User, oalias).options(joinedload('addresses'),
+ joinedload(oalias.items)).join(
('orders', oalias)).order_by(User.id, oalias.id).all()
eq_(ret, self._assert_result())
self.assert_sql_count(testing.db, go, 1)
sess.expunge_all()
def go():
d = sess.query(Node).filter_by(data='n1').\
- options(eagerload('children.children')).first()
+ options(joinedload('children.children')).first()
eq_(Node(data='n1', children=[
Node(data='n11'),
Node(data='n12', children=[
def go():
d = sess.query(Node).filter_by(data='n1').\
- options(eagerload('children.children')).first()
+ options(joinedload('children.children')).first()
# test that the query isn't wrapping the initial query for eager loading.
self.assert_sql_execution(testing.db, go,
eq_(
session.query(B).\
options(
- eagerload('parent_b1'),
- eagerload('parent_b2'),
- eagerload('parent_z')).
+ joinedload('parent_b1'),
+ joinedload('parent_b2'),
+ joinedload('parent_z')).
filter(B.id.in_([2, 8, 11])).order_by(B.id).all(),
[
B(id=2, parent_z=A(id=1), parent_b1=B(id=1), parent_b2=None),
(User(id=9, addresses=[Address(id=5)]), Order(id=4, items=[Item(id=1), Item(id=5)])),
],
sess.query(User, Order).filter(User.id==Order.user_id).\
- options(eagerload(User.addresses), eagerload(Order.items)).filter(User.id==9).\
+ options(joinedload(User.addresses), joinedload(Order.items)).filter(User.id==9).\
order_by(User.id, Order.id).all(),
)
self.assert_sql_count(testing.db, go, 1)
(User(id=9, addresses=[Address(id=5)]), Order(id=2, items=[Item(id=1), Item(id=2), Item(id=3)])),
(User(id=9, addresses=[Address(id=5)]), Order(id=4, items=[Item(id=1), Item(id=5)])),
],
- sess.query(User, Order).join(User.orders).options(eagerload(User.addresses), eagerload(Order.items)).filter(User.id==9).\
+ sess.query(User, Order).join(User.orders).options(joinedload(User.addresses), joinedload(Order.items)).filter(User.id==9).\
order_by(User.id, Order.id).all(),
)
self.assert_sql_count(testing.db, go, 1)
),
],
sess.query(User, Order, u1, o1).\
- join((Order, User.orders)).options(eagerload(User.addresses), eagerload(Order.items)).filter(User.id==9).\
- join((o1, u1.orders)).options(eagerload(u1.addresses), eagerload(o1.items)).filter(u1.id==7).\
+ join((Order, User.orders)).options(joinedload(User.addresses), joinedload(Order.items)).filter(User.id==9).\
+ join((o1, u1.orders)).options(joinedload(u1.addresses), joinedload(o1.items)).filter(u1.id==7).\
filter(Order.id<o1.id).\
order_by(User.id, Order.id, u1.id, o1.id).all(),
)
(User(id=9, addresses=[Address(id=5)]), Order(id=4, items=[Item(id=1), Item(id=5)])),
],
sess.query(User, oalias).filter(User.id==oalias.user_id).\
- options(eagerload(User.addresses), eagerload(oalias.items)).filter(User.id==9).\
+ options(joinedload(User.addresses), joinedload(oalias.items)).filter(User.id==9).\
order_by(User.id, oalias.id).all(),
)
self.assert_sql_count(testing.db, go, 1)
(User(id=9, addresses=[Address(id=5)]), Order(id=2, items=[Item(id=1), Item(id=2), Item(id=3)])),
(User(id=9, addresses=[Address(id=5)]), Order(id=4, items=[Item(id=1), Item(id=5)])),
],
- sess.query(User, oalias).join((User.orders, oalias)).options(eagerload(User.addresses), eagerload(oalias.items)).filter(User.id==9).\
+ sess.query(User, oalias).join((User.orders, oalias)).options(joinedload(User.addresses), joinedload(oalias.items)).filter(User.id==9).\
order_by(User.id, oalias.id).all(),
)
self.assert_sql_count(testing.db, go, 1)
# improper setup: oalias in the columns clause but join to usual
# orders alias. this should create two FROM clauses even though the
# query has a from_clause set up via the join
- self.assert_compile(sess.query(User, oalias).join(User.orders).options(eagerload(oalias.items)).with_labels().statement,
+ self.assert_compile(sess.query(User, oalias).join(User.orders).options(joinedload(oalias.items)).with_labels().statement,
"SELECT users.id AS users_id, users.name AS users_name, orders_1.id AS orders_1_id, "\
"orders_1.user_id AS orders_1_user_id, orders_1.address_id AS orders_1_address_id, "\
"orders_1.description AS orders_1_description, orders_1.isopen AS orders_1_isopen, items_1.id AS items_1_id, "\
"""
- # another argument for eagerload learning about inner joins
+ # another argument for joinedload learning about inner joins
__requires__ = ('correlated_outer_joins', )
sess = create_session()
def go():
eq_(
- sess.query(User).order_by(User.name).options(eagerload('stuff')).all(),
+ sess.query(User).order_by(User.name).options(joinedload('stuff')).all(),
[
User(name='user1', stuff=[Stuff(id=2)]),
User(name='user2', stuff=[Stuff(id=4)]),
sess = create_session()
def go():
eq_(
- sess.query(User).order_by(User.name).options(eagerload('stuff')).first(),
+ sess.query(User).order_by(User.name).options(joinedload('stuff')).first(),
User(name='user1', stuff=[Stuff(id=2)])
)
self.assert_sql_count(testing.db, go, 1)
sess = create_session()
def go():
eq_(
- sess.query(User).filter(User.id==2).options(eagerload('stuff')).one(),
+ sess.query(User).filter(User.id==2).options(joinedload('stuff')).one(),
User(name='user2', stuff=[Stuff(id=4)])
)
self.assert_sql_count(testing.db, go, 1)
assert len(u.addresses) == 2
@testing.resolve_artifact_names
- def test_eagerload_props_dontload(self):
+ def test_joinedload_props_dontload(self):
# relationships currently have to load separately from scalar instances.
# the use case is: expire "addresses". then access it. lazy load
# fires off to load "addresses", but needs foreign key or primary key
# attributes in order to lazy load; hits those attributes, such as
# below it hits "u.id". "u.id" triggers full unexpire operation,
- # eagerloads addresses since lazy=False. this is all wihtin lazy load
- # which fires unconditionally; so an unnecessary eagerload (or
+ # joinedloads addresses since lazy=False. this is all wihtin lazy load
+ # which fires unconditionally; so an unnecessary joinedload (or
# lazyload) was issued. would prefer not to complicate lazyloading to
# "figure out" that the operation should be aborted right now.
sess.expire(u, ['name', 'addresses'])
assert 'name' not in u.__dict__
assert 'addresses' not in u.__dict__
- (sess.query(User).options(sa.orm.eagerload('addresses')).
+ (sess.query(User).options(sa.orm.joinedload('addresses')).
filter_by(id=8).all())
assert 'name' in u.__dict__
assert 'addresses' in u.__dict__
self.assert_sql_count(testing.db, go, 1)
@testing.resolve_artifact_names
- def test_eagerload_query_refreshes(self):
+ def test_joinedload_query_refreshes(self):
mapper(User, users, properties={
'addresses':relationship(Address, backref='user', lazy=False),
})
sess = create_session()
u = (sess.query(User).
order_by(User.id).
- options(sa.orm.eagerload('adlist')).
+ options(sa.orm.joinedload('adlist')).
filter_by(name='jack')).one()
eq_(u.adlist,
[self.static.user_address_result[0].addresses[0]])
sess = create_session()
l = (sess.query(User).
order_by(User.id).
- options(sa.orm.eagerload('addresses'))).all()
+ options(sa.orm.joinedload('addresses'))).all()
def go():
eq_(l, self.static.user_address_result)
sess = create_session()
u = (sess.query(User).
- options(sa.orm.eagerload('addresses')).
+ options(sa.orm.joinedload('addresses')).
filter_by(id=8)).one()
def go():
sess = create_session()
oalias = aliased(Order)
- opt1 = sa.orm.eagerload(User.orders, Order.items)
+ opt1 = sa.orm.joinedload(User.orders, Order.items)
opt2a, opt2b = sa.orm.contains_eager(User.orders, Order.items, alias=oalias)
u1 = sess.query(User).join((oalias, User.orders)).options(opt1, opt2a, opt2b).first()
ustate = attributes.instance_state(u1)
def test_deep_options_1(self):
sess = create_session()
- # eagerload nothing.
+ # joinedload nothing.
u = sess.query(User).all()
def go():
x = u[0].orders[1].items[0].keywords[1]
def test_deep_options_2(self):
sess = create_session()
- # eagerload orders.items.keywords; eagerload_all() implies eager load
+ # joinedload orders.items.keywords; joinedload_all() implies eager load
# of orders, orders.items
l = (sess.query(User).
- options(sa.orm.eagerload_all('orders.items.keywords'))).all()
+ options(sa.orm.joinedload_all('orders.items.keywords'))).all()
def go():
x = l[0].orders[1].items[0].keywords[1]
self.sql_count_(0, go)
# same thing, with separate options calls
q2 = (sess.query(User).
- options(sa.orm.eagerload('orders')).
- options(sa.orm.eagerload('orders.items')).
- options(sa.orm.eagerload('orders.items.keywords')))
+ options(sa.orm.joinedload('orders')).
+ options(sa.orm.joinedload('orders.items')).
+ options(sa.orm.joinedload('orders.items.keywords')))
u = q2.all()
def go():
x = u[0].orders[1].items[0].keywords[1]
sa.exc.ArgumentError,
r"Can't find entity Mapper\|Order\|orders in Query. "
r"Current list: \['Mapper\|User\|users'\]",
- sess.query(User).options, sa.orm.eagerload(Order.items))
+ sess.query(User).options, sa.orm.joinedload(Order.items))
- # eagerload "keywords" on items. it will lazy load "orders", then
+ # joinedload "keywords" on items. it will lazy load "orders", then
# lazy load the "items" on the order, but on "items" it will eager
# load the "keywords"
- q3 = sess.query(User).options(sa.orm.eagerload('orders.items.keywords'))
+ q3 = sess.query(User).options(sa.orm.joinedload('orders.items.keywords'))
u = q3.all()
def go():
x = u[0].orders[1].items[0].keywords[1]
sess = create_session()
q3 = sess.query(User).options(
- sa.orm.eagerload(User.orders, Order.items, Item.keywords))
+ sa.orm.joinedload(User.orders, Order.items, Item.keywords))
u = q3.all()
def go():
x = u[0].orders[1].items[0].keywords[1]
)
@testing.resolve_artifact_names
- def test_eagerload_on_other(self):
+ def test_joinedload_on_other(self):
sess = create_session()
- child1s = sess.query(Child1).join(Child1.related).options(sa.orm.eagerload(Child1.related)).order_by(Child1.id)
+ child1s = sess.query(Child1).join(Child1.related).options(sa.orm.joinedload(Child1.related)).order_by(Child1.id)
def go():
eq_(
"SELECT base.id AS base_id, child2.id AS child2_id, base.type AS base_type "
"FROM base JOIN child2 ON base.id = child2.id WHERE base.id = :param_1",
-# eagerload- this shouldn't happen
+# joinedload- this shouldn't happen
# "SELECT base.id AS base_id, child2.id AS child2_id, base.type AS base_type, "
# "related_1.id AS related_1_id FROM base JOIN child2 ON base.id = child2.id "
# "LEFT OUTER JOIN related AS related_1 ON base.id = related_1.id WHERE base.id = :param_1",
)
@testing.resolve_artifact_names
- def test_eagerload_on_same(self):
+ def test_joinedload_on_same(self):
sess = create_session()
- child1s = sess.query(Child1).join(Child1.related).options(sa.orm.eagerload(Child1.child2, Child2.related)).order_by(Child1.id)
+ child1s = sess.query(Child1).join(Child1.related).options(sa.orm.joinedload(Child1.child2, Child2.related)).order_by(Child1.id)
def go():
eq_(
c1 = child1s[0]
- # this *does* eagerload
+ # this *does* joinedload
self.assert_sql_execution(
testing.db,
lambda: c1.child2,
self._test(thing)
@testing.resolve_artifact_names
- def test_eagerload_with_clear(self):
+ def test_joinedload_with_clear(self):
session = create_session()
- human = session.query(Human).options(sa.orm.eagerload("thing")).first()
+ human = session.query(Human).options(sa.orm.joinedload("thing")).first()
session.expunge_all()
thing = session.query(Thing).options(sa.orm.undefer("name")).first()
self._test(thing)
@testing.resolve_artifact_names
- def test_eagerload_no_clear(self):
+ def test_joinedload_no_clear(self):
session = create_session()
- human = session.query(Human).options(sa.orm.eagerload("thing")).first()
+ human = session.query(Human).options(sa.orm.joinedload("thing")).first()
thing = session.query(Thing).options(sa.orm.undefer("name")).first()
self._test(thing)
sess.expunge_all()
def go():
g2 = (sess.query(Graph).
- options(sa.orm.eagerload('edges'))).get([g.id, g.version_id])
+ options(sa.orm.joinedload('edges'))).get([g.id, g.version_id])
for e1, e2 in zip(g.edges, g2.edges):
eq_(e1.start, e2.start)
eq_(e1.end, e2.end)
h1.h2s.extend([H2(), H2()])
s.flush()
- h1s = s.query(H1).options(sa.orm.eagerload('h2s')).all()
+ h1s = s.query(H1).options(sa.orm.joinedload('h2s')).all()
eq_(len(h1s), 5)
self.assert_unordered_result(h1s, H1,
{'h2s': []},
{'h2s': (H2, [{'value': 'abc'}])})
- h1s = s.query(H1).options(sa.orm.eagerload('h3s')).all()
+ h1s = s.query(H1).options(sa.orm.joinedload('h3s')).all()
eq_(len(h1s), 5)
- h1s = s.query(H1).options(sa.orm.eagerload_all('t6a.h1b'),
- sa.orm.eagerload('h2s'),
- sa.orm.eagerload_all('h3s.h1s')).all()
+ h1s = s.query(H1).options(sa.orm.joinedload_all('t6a.h1b'),
+ sa.orm.joinedload('h2s'),
+ sa.orm.joinedload_all('h3s.h1s')).all()
eq_(len(h1s), 5)
@testing.resolve_artifact_names
sess.flush()
sess2 = create_session()
- u2 = sess2.query(User).options(sa.orm.eagerload('addresses')).get(7)
+ u2 = sess2.query(User).options(sa.orm.joinedload('addresses')).get(7)
sess3 = create_session()
u3 = sess3.merge(u2, load=False)
sess.expunge_all()
for opt in [
- sa.orm.eagerload(User.addresses),
- sa.orm.eagerload("addresses"),
+ sa.orm.joinedload(User.addresses),
+ sa.orm.joinedload("addresses"),
sa.orm.defer("name"),
sa.orm.defer(User.name),
- sa.orm.eagerload("addresses", User.addresses),
+ sa.orm.joinedload("addresses", User.addresses),
]:
opt2 = pickle.loads(pickle.dumps(opt))
eq_(opt.key, opt2.key)
assert u.orders[1].items[2].description == 'item 12'
# eager load does
- s.query(User).options(eagerload('addresses'), eagerload_all('orders.items')).populate_existing().all()
+ s.query(User).options(joinedload('addresses'), joinedload_all('orders.items')).populate_existing().all()
assert u.addresses[0].email_address == 'jack@bean.com'
assert u.orders[1].items[2].description == 'item 5'
[(7, 1), (8, 3), (9, 1)]
)
- def test_no_eagerload(self):
- """test that eagerloads are pushed outwards and not rendered in subqueries."""
+ def test_no_joinedload(self):
+ """test that joinedloads are pushed outwards and not rendered in subqueries."""
s = create_session()
oracle_as = not testing.against('oracle') and "AS " or ""
self.assert_compile(
- s.query(User).options(eagerload(User.addresses)).from_self().statement,
+ s.query(User).options(joinedload(User.addresses)).from_self().statement,
"SELECT anon_1.users_id, anon_1.users_name, addresses_1.id, addresses_1.user_id, "\
"addresses_1.email_address FROM (SELECT users.id AS users_id, users.name AS users_name FROM users) %(oracle_as)sanon_1 "\
"LEFT OUTER JOIN addresses %(oracle_as)saddresses_1 ON anon_1.users_id = addresses_1.user_id ORDER BY addresses_1.id" % {
)
eq_(
- sess.query(User, Address).filter(User.id==Address.user_id).filter(Address.id.in_([2, 5])).from_self().options(eagerload('addresses')).first(),
+ sess.query(User, Address).filter(User.id==Address.user_id).filter(Address.id.in_([2, 5])).from_self().options(joinedload('addresses')).first(),
# order_by(User.id, Address.id).first(),
(User(id=8, addresses=[Address(), Address(), Address()]), Address(id=2)),
def go():
eq_(
- fred.union(ed).order_by(User.name).options(eagerload(User.addresses)).all(),
+ fred.union(ed).order_by(User.name).options(joinedload(User.addresses)).all(),
[
User(name='ed', addresses=[Address(), Address(), Address()]),
User(name='fred', addresses=[Address()])
sess.expunge_all()
- # test that it works on embedded eagerload/LIMIT subquery
- q = sess.query(User).join('addresses').distinct().options(eagerload('addresses')).order_by(desc(Address.email_address)).limit(2)
+ # test that it works on embedded joinedload/LIMIT subquery
+ q = sess.query(User).join('addresses').distinct().options(joinedload('addresses')).order_by(desc(Address.email_address)).limit(2)
def go():
assert [
q = sess.query(User)
def go():
# outerjoin to User.orders, offset 1/limit 2 so we get user 7 + second two orders.
- # then eagerload the addresses. User + Order columns go into the subquery, address
- # left outer joins to the subquery, eagerloader for User.orders applies context.adapter
+ # then joinedload the addresses. User + Order columns go into the subquery, address
+ # left outer joins to the subquery, joinedloader for User.orders applies context.adapter
# to result rows. This was [ticket:1180].
- l = q.outerjoin(User.orders).options(eagerload(User.addresses), contains_eager(User.orders)).order_by(User.id, Order.id).offset(1).limit(2).all()
+ l = q.outerjoin(User.orders).options(joinedload(User.addresses), contains_eager(User.orders)).order_by(User.id, Order.id).offset(1).limit(2).all()
eq_(l, [User(id=7,
addresses=[Address(email_address=u'jack@bean.com',user_id=7,id=1)],
name=u'jack',
# same as above, except Order is aliased, so two adapters are applied by the
# eager loader
oalias = aliased(Order)
- l = q.outerjoin((User.orders, oalias)).options(eagerload(User.addresses), contains_eager(User.orders, alias=oalias)).order_by(User.id, oalias.id).offset(1).limit(2).all()
+ l = q.outerjoin((User.orders, oalias)).options(joinedload(User.addresses), contains_eager(User.orders, alias=oalias)).order_by(User.id, oalias.id).offset(1).limit(2).all()
eq_(l, [User(id=7,
addresses=[Address(email_address=u'jack@bean.com',user_id=7,id=1)],
name=u'jack',
# test eager aliasing, with/without select_from aliasing
for q in [
- sess.query(User, adalias.email_address).outerjoin((User.addresses, adalias)).options(eagerload(User.addresses)).order_by(User.id, adalias.id).limit(10),
- sess.query(User, adalias.email_address, adalias.id).outerjoin((User.addresses, adalias)).from_self(User, adalias.email_address).options(eagerload(User.addresses)).order_by(User.id, adalias.id).limit(10),
+ sess.query(User, adalias.email_address).outerjoin((User.addresses, adalias)).options(joinedload(User.addresses)).order_by(User.id, adalias.id).limit(10),
+ sess.query(User, adalias.email_address, adalias.id).outerjoin((User.addresses, adalias)).from_self(User, adalias.email_address).options(joinedload(User.addresses)).order_by(User.id, adalias.id).limit(10),
]:
eq_(
(User(addresses=[],name=u'chuck',id=10), None)]
)
- def test_column_from_limited_eagerload(self):
+ def test_column_from_limited_joinedload(self):
sess = create_session()
def go():
- results = sess.query(User).limit(1).options(eagerload('addresses')).add_column(User.name).all()
+ results = sess.query(User).limit(1).options(joinedload('addresses')).add_column(User.name).all()
eq_(results, [(User(name='jack'), 'jack')])
self.assert_sql_count(testing.db, go, 1)
sess.query(oalias, Order).from_self().filter(oalias.user_id==Order.user_id).filter(oalias.user_id==7).filter(Order.id<oalias.id).order_by(oalias.id, Order.id),
# here we go....two layers of aliasing
- sess.query(Order, oalias).filter(Order.user_id==oalias.user_id).filter(Order.user_id==7).filter(Order.id>oalias.id).from_self().order_by(Order.id, oalias.id).limit(10).options(eagerload(Order.items)),
+ sess.query(Order, oalias).filter(Order.user_id==oalias.user_id).filter(Order.user_id==7).filter(Order.id>oalias.id).from_self().order_by(Order.id, oalias.id).limit(10).options(joinedload(Order.items)),
# gratuitous four layers
- sess.query(Order, oalias).filter(Order.user_id==oalias.user_id).filter(Order.user_id==7).filter(Order.id>oalias.id).from_self().from_self().from_self().order_by(Order.id, oalias.id).limit(10).options(eagerload(Order.items)),
+ sess.query(Order, oalias).filter(Order.user_id==oalias.user_id).filter(Order.user_id==7).filter(Order.id>oalias.id).from_self().from_self().from_self().order_by(Order.id, oalias.id).limit(10).options(joinedload(Order.items)),
]:
eq_(q.all(), [(user8, address3)])
sess.expunge_all()
- q = sess.query(User, address_entity).join(('addresses', address_entity)).options(eagerload('addresses')).filter_by(email_address='ed@bettyboop.com')
+ q = sess.query(User, address_entity).join(('addresses', address_entity)).options(joinedload('addresses')).filter_by(email_address='ed@bettyboop.com')
eq_(list(util.OrderedSet(q.all())), [(user8, address3)])
sess.expunge_all()
User(name='ed',id=8), User(name='jack',id=7)
])
- eq_(sess.query(User).select_from(sel).options(eagerload('addresses')).first(),
+ eq_(sess.query(User).select_from(sel).options(joinedload('addresses')).first(),
User(name='jack', addresses=[Address(id=1)])
)
def go():
eq_(
sess.query(User).select_from(sel).
- options(eagerload_all('orders.items.keywords')).
+ options(joinedload_all('orders.items.keywords')).
join('orders', 'items', 'keywords', aliased=True).
filter(Keyword.name.in_(['red', 'big', 'round'])).all(),
[
sess = create_session()
def go():
- eq_(sess.query(User).options(eagerload('addresses')).select_from(sel).order_by(User.id).all(),
+ eq_(sess.query(User).options(joinedload('addresses')).select_from(sel).order_by(User.id).all(),
[
User(id=7, addresses=[Address(id=1)]),
User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)])
sess.expunge_all()
def go():
- eq_(sess.query(User).options(eagerload('addresses')).select_from(sel).filter(User.id==8).order_by(User.id).all(),
+ eq_(sess.query(User).options(joinedload('addresses')).select_from(sel).filter(User.id==8).order_by(User.id).all(),
[User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)])]
)
self.assert_sql_count(testing.db, go, 1)
sess.expunge_all()
def go():
- eq_(sess.query(User).options(eagerload('addresses')).select_from(sel).order_by(User.id)[1], User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)]))
+ eq_(sess.query(User).options(joinedload('addresses')).select_from(sel).order_by(User.id)[1], User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)]))
self.assert_sql_count(testing.db, go, 1)
class CustomJoinTest(QueryTest):
join((Node.parent, parent), (parent.parent, grandparent)).\
filter(Node.data=='n122').filter(parent.data=='n12').\
filter(grandparent.data=='n1').\
- options(eagerload(Node.children)).first(),
+ options(joinedload(Node.children)).first(),
(Node(data='n122'), Node(data='n12'), Node(data='n1'))
)
join((Node.parent, parent), (parent.parent, grandparent)).\
filter(Node.data=='n122').filter(parent.data=='n12').\
filter(grandparent.data=='n1').from_self().\
- options(eagerload(Node.children)).first(),
+ options(joinedload(Node.children)).first(),
(Node(data='n122'), Node(data='n12'), Node(data='n1'))
)
sess = create_session()
- sess.query(Address).options(eagerload('user')).all()
+ sess.query(Address).options(joinedload('user')).all()
eq_(sess.query(User).all(),
[
for x in range(2):
sess.expunge_all()
def go():
- eq_(sess.query(Address).options(eagerload('user')).order_by(Address.id).all(), address_result)
+ eq_(sess.query(Address).options(joinedload('user')).order_by(Address.id).all(), address_result)
self.assert_sql_count(testing.db, go, 1)
ualias = aliased(User)
ua = aliased(User)
eq_(sess.query(Address, ua.concat, ua.count).
select_from(join(Address, ua, 'user')).
- options(eagerload(Address.user)).order_by(Address.id).all(),
+ options(joinedload(Address.user)).order_by(Address.id).all(),
[
(Address(id=1, user=User(id=7, concat=14, count=1)), 14, 1),
(Address(id=2, user=User(id=8, concat=16, count=3)), 16, 3),
[(1, 7, 14, 1), (2, 8, 16, 3), (3, 8, 16, 3), (4, 8, 16, 3), (5, 9, 18, 1)]
)
- def test_external_columns_eagerload(self):
+ def test_external_columns_joinedload(self):
# in this test, we have a subquery on User that accesses "addresses", underneath
- # an eagerload for "addresses". So the "addresses" alias adapter needs to *not* hit
+ # an joinedload for "addresses". So the "addresses" alias adapter needs to *not* hit
# the "addresses" table within the "user" subquery, but "user" still needs to be adapted.
# therefore the long standing practice of eager adapters being "chained" has been removed
# since its unnecessary and breaks this exact condition.
sess = create_session()
def go():
- o1 = sess.query(Order).options(eagerload_all('address.user')).get(1)
+ o1 = sess.query(Order).options(joinedload_all('address.user')).get(1)
eq_(o1.address.user.count, 1)
self.assert_sql_count(testing.db, go, 1)
sess = create_session()
def go():
- o1 = sess.query(Order).options(eagerload_all('address.user')).first()
+ o1 = sess.query(Order).options(joinedload_all('address.user')).first()
eq_(o1.address.user.count, 1)
self.assert_sql_count(testing.db, go, 1)
eq_(sess.query(Document.title).order_by(Document.id).all(), zip(['foofoo','barbar', 'baz']))
@testing.resolve_artifact_names
- def test_update_with_explicit_eagerload(self):
+ def test_update_with_explicit_joinedload(self):
sess = create_session(bind=testing.db, autocommit=False)
john,jack,jill,jane = sess.query(User).order_by(User.id).all()
- sess.query(User).options(eagerload(User.documents)).filter(User.age > 29).update({'age': User.age - 10}, synchronize_session='fetch')
+ sess.query(User).options(joinedload(User.documents)).filter(User.age > 29).update({'age': User.age - 10}, synchronize_session='fetch')
eq_([john.age, jack.age, jill.age, jane.age], [25,37,29,27])
eq_(sess.query(User.age).order_by(User.id).all(), zip([25,37,29,27]))
from sqlalchemy.test import engines, testing, config
from sqlalchemy import Integer, String, Sequence
from sqlalchemy.test.schema import Table, Column
-from sqlalchemy.orm import mapper, relationship, backref, eagerload
+from sqlalchemy.orm import mapper, relationship, backref, joinedload
from sqlalchemy.test.testing import eq_
from test.engine import _base as engine_base
from test.orm import _base, _fixtures
s.add(User(name="ed", addresses=[Address(email_address="ed1")]))
s.commit()
- user = s.query(User).options(eagerload(User.addresses)).one()
+ user = s.query(User).options(joinedload(User.addresses)).one()
user.addresses[0].user # lazyload
eq_(user, User(name="ed", addresses=[Address(email_address="ed1")]))
gc_collect()
assert len(s.identity_map) == 0
- user = s.query(User).options(eagerload(User.addresses)).one()
+ user = s.query(User).options(joinedload(User.addresses)).one()
user.addresses[0].email_address='ed2'
user.addresses[0].user # lazyload
del user
assert len(s.identity_map) == 2
s.commit()
- user = s.query(User).options(eagerload(User.addresses)).one()
+ user = s.query(User).options(joinedload(User.addresses)).one()
eq_(user, User(name="ed", addresses=[Address(email_address="ed2")]))
@testing.resolve_artifact_names
s.add(User(name="ed", address=Address(email_address="ed1")))
s.commit()
- user = s.query(User).options(eagerload(User.address)).one()
+ user = s.query(User).options(joinedload(User.address)).one()
user.address.user
eq_(user, User(name="ed", address=Address(email_address="ed1")))
gc_collect()
assert len(s.identity_map) == 0
- user = s.query(User).options(eagerload(User.address)).one()
+ user = s.query(User).options(joinedload(User.address)).one()
user.address.email_address='ed2'
user.address.user # lazyload
assert len(s.identity_map) == 2
s.commit()
- user = s.query(User).options(eagerload(User.address)).one()
+ user = s.query(User).options(joinedload(User.address)).one()
eq_(user, User(name="ed", address=Address(email_address="ed2")))
@testing.resolve_artifact_names
from sqlalchemy import Integer, String, ForeignKey
from sqlalchemy.orm import backref, subqueryload, subqueryload_all, \
mapper, relationship, clear_mappers,\
- create_session, lazyload, aliased, eagerload,\
+ create_session, lazyload, aliased, joinedload,\
deferred, undefer
from sqlalchemy.test.testing import eq_, assert_raises
from sqlalchemy.test.assertsql import CompiledSQL
( "lazyload", "lazyload", "lazyload", 15 ),
("subqueryload", "lazyload", "lazyload", 12),
("subqueryload", "subqueryload", "lazyload", 8),
- ("eagerload", "subqueryload", "lazyload", 7),
+ ("joinedload", "subqueryload", "lazyload", 7),
("lazyload", "lazyload", "subqueryload", 12),
("subqueryload", "subqueryload", "subqueryload", 4),
- ("subqueryload", "subqueryload", "eagerload", 3),
+ ("subqueryload", "subqueryload", "joinedload", 3),
]
-# _pathing_runs = [("subqueryload", "subqueryload", "eagerload", 3)]
+# _pathing_runs = [("subqueryload", "subqueryload", "joinedload", 3)]
# _pathing_runs = [("subqueryload", "subqueryload", "subqueryload", 4)]
def test_options_pathing(self):
mapper(Keyword, keywords)
callables = {
- 'eagerload':eagerload,
+ 'joinedload':joinedload,
'subqueryload':subqueryload
}
def _do_mapper_test(self, configs):
opts = {
'lazyload':'select',
- 'eagerload':'joined',
+ 'joinedload':'joined',
'subqueryload':'subquery',
}
assert new_a1.t2s[0].d == b1.d
session.expunge_all()
- new_a1 = (session.query(A).options(sa.orm.eagerload('t2s')).
+ new_a1 = (session.query(A).options(sa.orm.joinedload('t2s')).
filter(t1.c.a == a1.a)).one()
assert new_a1.a == a1.a
assert new_a1.t2s[0].d == b1.d
#print l
subitems.insert().execute(*l)
-@profiling.profiled('masseagerload', always=True, sort=['cumulative'])
-def masseagerload(session):
+@profiling.profiled('massjoinedload', always=True, sort=['cumulative'])
+def massjoinedload(session):
session.begin()
query = session.query(Item)
l = query.all()
meta.create_all()
try:
load()
- masseagerload(create_session())
+ massjoinedload(create_session())
finally:
meta.drop_all()
q = session.query(Purchase). \
order_by(desc(Purchase.purchase_date)). \
limit(50).\
- options(eagerload('items'), eagerload('items.subitems'),
- eagerload('customer'))
+ options(joinedload('items'), joinedload('items.subitems'),
+ joinedload('customer'))
report = []
# "write" the report. pretend it's going to a web template or something,