diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-24 19:11:01 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-24 19:11:01 -0400 |
commit | 1675811029553501bb23084604c64d974dfe739c (patch) | |
tree | 7e3c29f55c562aab06a48b6b4727f59156ed6722 | |
parent | 290a1596ce6f806aa6f25dd754cf0d2197f160ff (diff) | |
download | sqlalchemy-1675811029553501bb23084604c64d974dfe739c.tar.gz |
- 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.
- Added documentation to tutorial,mapper doc, api docs
for subqueryload, subqueryload_all, and other options.
26 files changed, 439 insertions, 250 deletions
@@ -21,6 +21,18 @@ CHANGES 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 diff --git a/doc/build/mappers.rst b/doc/build/mappers.rst index c6ae0c85d..7eb809cbe 100644 --- a/doc/build/mappers.rst +++ b/doc/build/mappers.rst @@ -323,7 +323,7 @@ The "default" ordering for a collection, which applies to list-based collections '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) @@ -1198,12 +1198,12 @@ To add criterion to multiple points along a longer join, use ``from_joinpoint=Tr 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() @@ -1531,14 +1531,18 @@ The ORM uses this approach for built-ins, quietly substituting a trivial subclas 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 @@ -1551,65 +1555,149 @@ By default, all inter-object relationships are **lazy loading**. The scalar or .. 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={ diff --git a/doc/build/ormtutorial.rst b/doc/build/ormtutorial.rst index 616adae00..2ae760e99 100644 --- a/doc/build/ormtutorial.rst +++ b/doc/build/ormtutorial.rst @@ -809,14 +809,14 @@ Let's look at the ``addresses`` collection. Watch the SQL: 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 @@ -831,12 +831,12 @@ If you want to reduce the number of queries (dramatically, in many cases), we ca >>> 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 @@ -898,10 +898,10 @@ the :meth:`~sqlalchemy.orm.query.Query.select_from` method to set an explicit FR 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 diff --git a/doc/build/reference/orm/query.rst b/doc/build/reference/orm/query.rst index b209f6fef..6ff9048bc 100644 --- a/doc/build/reference/orm/query.rst +++ b/doc/build/reference/orm/query.rst @@ -44,7 +44,15 @@ Options which are passed to ``query.options()``, to affect the behavior of loadi .. autofunction:: extension +.. autofunction:: joinedload + +.. autofunction:: joinedload_all + .. autofunction:: lazyload +.. autofunction:: subqueryload + +.. autofunction:: subqueryload_all + .. autofunction:: undefer diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index c9ed3cf2e..fb05f4181 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -83,6 +83,8 @@ __all__ = ( 'eagerload_all', 'extension', 'join', + 'joinedload', + 'joinedload_all', 'lazyload', 'mapper', 'make_transient', @@ -296,16 +298,21 @@ def relationship(argument, secondary=None, **kwargs): 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. @@ -315,7 +322,13 @@ def relationship(argument, secondary=None, **kwargs): ``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. @@ -906,35 +919,42 @@ def extension(ext): 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: @@ -946,26 +966,33 @@ def eagerload(*keys, **kw): 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: @@ -976,11 +1003,63 @@ def eagerload_all(*keys, **kw): 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): @@ -989,6 +1068,8 @@ def lazyload(*keys): Used with :meth:`~sqlalchemy.orm.query.Query.options`. + See also: :func:`eagerload`, :func:`subqueryload` + """ return strategies.EagerLazyOption(keys, lazy=True) @@ -998,6 +1079,8 @@ def noload(*keys): Used with :meth:`~sqlalchemy.orm.query.Query.options`. + See also: :func:`lazyload`, :func:`eagerload`, :func:`subqueryload` + """ return strategies.EagerLazyOption(keys, lazy=None) diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 4b6770861..80d101b78 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -391,22 +391,14 @@ class RelationshipProperty(StrategizedProperty): 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() diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 0665bdcb3..f4f3b5821 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -1125,28 +1125,34 @@ log.class_logger(EagerLoader) 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): diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index aa04cdf11..a6f3f7a79 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -6,7 +6,7 @@ import sqlalchemy as sa 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 @@ -406,7 +406,7 @@ class DeclarativeTest(DeclarativeTestBase): 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'), ])]) diff --git a/test/ext/test_serializer.py b/test/ext/test_serializer.py index f06677eb9..4e26e5b9a 100644 --- a/test/ext/test_serializer.py +++ b/test/ext/test_serializer.py @@ -6,7 +6,7 @@ from sqlalchemy.test import testing 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 @@ -97,7 +97,7 @@ class SerializeTest(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) diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index 9477fddab..904660e77 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -455,7 +455,7 @@ class EagerTargetingTest(_base.MappedTest): 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')) diff --git a/test/orm/inheritance/test_concrete.py b/test/orm/inheritance/test_concrete.py index be921b00b..a8fe06867 100644 --- a/test/orm/inheritance/test_concrete.py +++ b/test/orm/inheritance/test_concrete.py @@ -313,7 +313,7 @@ class ConcreteTest(_base.MappedTest): 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) @@ -462,7 +462,7 @@ class PropertyInheritanceTest(_base.MappedTest): 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) diff --git a/test/orm/inheritance/test_polymorph2.py b/test/orm/inheritance/test_polymorph2.py index 94939b33c..ffe6201de 100644 --- a/test/orm/inheritance/test_polymorph2.py +++ b/test/orm/inheritance/test_polymorph2.py @@ -384,7 +384,7 @@ class RelationshipTest4(_base.MappedTest): 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) @@ -407,7 +407,7 @@ class RelationshipTest4(_base.MappedTest): # 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) diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py index 7be6de6e3..239f2e45a 100644 --- a/test/orm/inheritance/test_query.py +++ b/test/orm/inheritance/test_query.py @@ -195,11 +195,11 @@ def _produce_test(select_type): 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() @@ -211,13 +211,13 @@ def _produce_test(select_type): 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) @@ -506,16 +506,16 @@ def _produce_test(select_type): 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() @@ -528,11 +528,11 @@ def _produce_test(select_type): 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) @@ -1170,7 +1170,7 @@ class SelfReferentialM2MTest(_base.MappedTest, AssertsCompiledSQL): 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, @@ -1202,7 +1202,7 @@ class SelfReferentialM2MTest(_base.MappedTest, AssertsCompiledSQL): 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' @@ -1259,11 +1259,11 @@ class EagerToSubclassTest(_base.MappedTest): 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')]) @@ -1286,7 +1286,7 @@ class EagerToSubclassTest(_base.MappedTest): 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' @@ -1352,11 +1352,11 @@ class SubClassEagerToSubClassTest(_base.MappedTest): 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')]) @@ -1367,7 +1367,7 @@ class SubClassEagerToSubClassTest(_base.MappedTest): 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')]) diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index f54044d64..4b7078eb5 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -286,7 +286,7 @@ class RelationshipToSingleTest(MappedTest): # 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')]) diff --git a/test/orm/test_assorted_eager.py b/test/orm/test_assorted_eager.py index 7fa3b6b8a..e58a61329 100644 --- a/test/orm/test_assorted_eager.py +++ b/test/orm/test_assorted_eager.py @@ -124,7 +124,7 @@ class EagerTest(_base.MappedTest): 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, @@ -139,15 +139,15 @@ class EagerTest(_base.MappedTest): 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 == @@ -163,9 +163,9 @@ class EagerTest(_base.MappedTest): @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, @@ -179,7 +179,7 @@ class EagerTest(_base.MappedTest): @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)). @@ -191,7 +191,7 @@ class EagerTest(_base.MappedTest): @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)) @@ -337,7 +337,7 @@ class EagerTest3(_base.MappedTest): # 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)) @@ -840,7 +840,7 @@ class EagerTest9(_base.MappedTest): @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') @@ -864,7 +864,7 @@ class EagerTest9(_base.MappedTest): # "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 diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py index 81537a8d9..f6e8e8143 100644 --- a/test/orm/test_eager_relations.py +++ b/test/orm/test_eager_relations.py @@ -3,7 +3,7 @@ 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 @@ -40,7 +40,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): 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) @@ -259,20 +259,20 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): 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() @@ -320,7 +320,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): 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) @@ -622,7 +622,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): 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 " @@ -633,7 +633,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ) 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 " @@ -642,7 +642,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ) 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 " @@ -651,7 +651,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ) 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, " @@ -664,7 +664,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ) 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 " @@ -679,7 +679,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ) 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, " @@ -821,7 +821,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): 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): @@ -905,14 +905,14 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): )) 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, " @@ -925,8 +925,8 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): 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, @@ -943,7 +943,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): # 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 " @@ -1038,15 +1038,15 @@ class AddEntityTest(_fixtures.FixtureTest): 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) @@ -1268,7 +1268,7 @@ class SelfReferentialEagerTest(_base.MappedTest): 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=[ @@ -1282,7 +1282,7 @@ class SelfReferentialEagerTest(_base.MappedTest): 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, @@ -1392,9 +1392,9 @@ class MixedSelfReferentialEagerTest(_base.MappedTest): 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), @@ -1475,7 +1475,7 @@ class MixedEntitiesTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): (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) @@ -1487,7 +1487,7 @@ class MixedEntitiesTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): (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) @@ -1525,8 +1525,8 @@ class MixedEntitiesTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), ], 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(), ) @@ -1548,7 +1548,7 @@ class MixedEntitiesTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): (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) @@ -1560,7 +1560,7 @@ class MixedEntitiesTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): (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) @@ -1570,7 +1570,7 @@ class MixedEntitiesTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): # 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, "\ @@ -1710,7 +1710,7 @@ class CorrelatedSubqueryTest(_base.MappedTest): """ - # another argument for eagerload learning about inner joins + # another argument for joinedload learning about inner joins __requires__ = ('correlated_outer_joins', ) @@ -1826,7 +1826,7 @@ class CorrelatedSubqueryTest(_base.MappedTest): 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)]), @@ -1846,7 +1846,7 @@ class CorrelatedSubqueryTest(_base.MappedTest): 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) @@ -1854,7 +1854,7 @@ class CorrelatedSubqueryTest(_base.MappedTest): 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) diff --git a/test/orm/test_expire.py b/test/orm/test_expire.py index e801fe6e8..e0e32e63e 100644 --- a/test/orm/test_expire.py +++ b/test/orm/test_expire.py @@ -373,14 +373,14 @@ class ExpireTest(_fixtures.FixtureTest): 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. @@ -568,7 +568,7 @@ class ExpireTest(_fixtures.FixtureTest): 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__ @@ -641,7 +641,7 @@ class ExpireTest(_fixtures.FixtureTest): 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), }) diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index dbca51980..4bb58cbeb 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -1095,7 +1095,7 @@ class OptionsTest(_fixtures.FixtureTest): 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]]) @@ -1111,7 +1111,7 @@ class OptionsTest(_fixtures.FixtureTest): 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) @@ -1125,7 +1125,7 @@ class OptionsTest(_fixtures.FixtureTest): sess = create_session() u = (sess.query(User). - options(sa.orm.eagerload('addresses')). + options(sa.orm.joinedload('addresses')). filter_by(id=8)).one() def go(): @@ -1252,7 +1252,7 @@ class OptionsTest(_fixtures.FixtureTest): 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) @@ -1284,7 +1284,7 @@ class DeepOptionsTest(_fixtures.FixtureTest): 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] @@ -1294,10 +1294,10 @@ class DeepOptionsTest(_fixtures.FixtureTest): 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) @@ -1309,9 +1309,9 @@ class DeepOptionsTest(_fixtures.FixtureTest): # 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] @@ -1325,12 +1325,12 @@ class DeepOptionsTest(_fixtures.FixtureTest): 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] @@ -1338,7 +1338,7 @@ class DeepOptionsTest(_fixtures.FixtureTest): 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] @@ -1858,10 +1858,10 @@ class SecondaryOptionsTest(_base.MappedTest): ) @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_( @@ -1879,7 +1879,7 @@ class SecondaryOptionsTest(_base.MappedTest): "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", @@ -1888,10 +1888,10 @@ class SecondaryOptionsTest(_base.MappedTest): ) @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_( @@ -1902,7 +1902,7 @@ class SecondaryOptionsTest(_base.MappedTest): c1 = child1s[0] - # this *does* eagerload + # this *does* joinedload self.assert_sql_execution( testing.db, lambda: c1.child2, @@ -1972,17 +1972,17 @@ class DeferredPopulationTest(_base.MappedTest): 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) @@ -2092,7 +2092,7 @@ class CompositeTypesTest(_base.MappedTest): 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) @@ -2708,7 +2708,7 @@ class RequirementsTest(_base.MappedTest): 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, @@ -2720,12 +2720,12 @@ class RequirementsTest(_base.MappedTest): {'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 diff --git a/test/orm/test_merge.py b/test/orm/test_merge.py index 9f8861ad8..e80b92699 100644 --- a/test/orm/test_merge.py +++ b/test/orm/test_merge.py @@ -708,7 +708,7 @@ class MergeTest(_fixtures.FixtureTest): 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) diff --git a/test/orm/test_pickled.py b/test/orm/test_pickled.py index abe4c91a2..4cdfa4181 100644 --- a/test/orm/test_pickled.py +++ b/test/orm/test_pickled.py @@ -205,11 +205,11 @@ class PickleTest(_fixtures.FixtureTest): 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) diff --git a/test/orm/test_query.py b/test/orm/test_query.py index c31c36183..113f8f1f2 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -209,7 +209,7 @@ class GetTest(QueryTest): 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' @@ -882,15 +882,15 @@ class FromSelfTest(QueryTest, AssertsCompiledSQL): [(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" % { @@ -947,7 +947,7 @@ class FromSelfTest(QueryTest, AssertsCompiledSQL): ) 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)), @@ -1064,7 +1064,7 @@ class SetOpsTest(QueryTest, AssertsCompiledSQL): 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()]) @@ -1161,8 +1161,8 @@ class DistinctTest(QueryTest): 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 [ @@ -2533,10 +2533,10 @@ class InstancesTest(QueryTest, AssertsCompiledSQL): 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', @@ -2551,7 +2551,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL): # 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', @@ -2767,8 +2767,8 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): # 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_( @@ -2791,11 +2791,11 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): (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) @@ -2813,10 +2813,10 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): 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)), ]: @@ -2877,7 +2877,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL): 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() @@ -3125,7 +3125,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL): 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)]) ) @@ -3270,7 +3270,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL): 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(), [ @@ -3311,7 +3311,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL): 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)]) @@ -3321,14 +3321,14 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL): 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): @@ -3590,7 +3590,7 @@ class SelfReferentialTest(_base.MappedTest, AssertsCompiledSQL): 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')) ) @@ -3599,7 +3599,7 @@ class SelfReferentialTest(_base.MappedTest, AssertsCompiledSQL): 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')) ) @@ -3732,7 +3732,7 @@ class ExternalColumnsTest(QueryTest): sess = create_session() - sess.query(Address).options(eagerload('user')).all() + sess.query(Address).options(joinedload('user')).all() eq_(sess.query(User).all(), [ @@ -3756,7 +3756,7 @@ class ExternalColumnsTest(QueryTest): 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) @@ -3789,7 +3789,7 @@ class ExternalColumnsTest(QueryTest): 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), @@ -3807,9 +3807,9 @@ class ExternalColumnsTest(QueryTest): [(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. @@ -3825,13 +3825,13 @@ class ExternalColumnsTest(QueryTest): 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) @@ -4161,11 +4161,11 @@ class UpdateDeleteTest(_base.MappedTest): 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])) diff --git a/test/orm/test_session.py b/test/orm/test_session.py index 831707278..fca3bf757 100644 --- a/test/orm/test_session.py +++ b/test/orm/test_session.py @@ -8,7 +8,7 @@ import sqlalchemy as sa 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 @@ -889,7 +889,7 @@ class SessionTest(_fixtures.FixtureTest): 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")])) @@ -897,7 +897,7 @@ class SessionTest(_fixtures.FixtureTest): 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 @@ -905,7 +905,7 @@ class SessionTest(_fixtures.FixtureTest): 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 @@ -918,7 +918,7 @@ class SessionTest(_fixtures.FixtureTest): 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"))) @@ -926,7 +926,7 @@ class SessionTest(_fixtures.FixtureTest): 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 @@ -935,7 +935,7 @@ class SessionTest(_fixtures.FixtureTest): 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 diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index 28b7e4397..eca8e9af1 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -4,7 +4,7 @@ from sqlalchemy.test.schema import Table, Column 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 @@ -171,12 +171,12 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ( "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): @@ -202,7 +202,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): mapper(Keyword, keywords) callables = { - 'eagerload':eagerload, + 'joinedload':joinedload, 'subqueryload':subqueryload } @@ -221,7 +221,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): def _do_mapper_test(self, configs): opts = { 'lazyload':'select', - 'eagerload':'joined', + 'joinedload':'joined', 'subqueryload':'subquery', } diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py index 1a0158035..d79634a64 100644 --- a/test/orm/test_unitofwork.py +++ b/test/orm/test_unitofwork.py @@ -173,7 +173,7 @@ class UnicodeSchemaTest(engine_base.AltEngineTest, _base.MappedTest): 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 diff --git a/test/perf/masseagerload.py b/test/perf/masseagerload.py index 3d251c816..ab3f00fa4 100644 --- a/test/perf/masseagerload.py +++ b/test/perf/masseagerload.py @@ -35,8 +35,8 @@ def load(): #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() @@ -46,7 +46,7 @@ def all(): meta.create_all() try: load() - masseagerload(create_session()) + massjoinedload(create_session()) finally: meta.drop_all() diff --git a/test/perf/ormsession.py b/test/perf/ormsession.py index fc3e2e206..0df440aab 100644 --- a/test/perf/ormsession.py +++ b/test/perf/ormsession.py @@ -152,8 +152,8 @@ def run_queries(): 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, |