diff options
Diffstat (limited to 'doc/build/orm/loading.rst')
-rw-r--r-- | doc/build/orm/loading.rst | 181 |
1 files changed, 99 insertions, 82 deletions
diff --git a/doc/build/orm/loading.rst b/doc/build/orm/loading.rst index e84179558..7be25de4a 100644 --- a/doc/build/orm/loading.rst +++ b/doc/build/orm/loading.rst @@ -1,3 +1,5 @@ +.. _loading_toplevel: + .. currentmodule:: sqlalchemy.orm Relationship Loading Techniques @@ -82,24 +84,25 @@ The default **loader strategy** for any :func:`~sqlalchemy.orm.relationship` is configured by the ``lazy`` keyword argument, which defaults to ``select`` - this indicates a "select" statement . Below we set it as ``joined`` so that the ``children`` relationship is eager -loading, using a join: - -.. sourcecode:: python+sql +loaded using a JOIN:: # load the 'children' collection using LEFT OUTER JOIN - mapper(Parent, parent_table, properties={ - 'children': relationship(Child, lazy='joined') - }) + class Parent(Base): + __tablename__ = 'parent' + + id = Column(Integer, primary_key=True) + children = relationship("Child", lazy='joined') We can also set it to eagerly load using a second query for all collections, -using ``subquery``: +using ``subquery``:: -.. sourcecode:: python+sql + # load the 'children' collection using a second query which + # JOINS to a subquery of the original + class Parent(Base): + __tablename__ = 'parent' - # load the 'children' attribute using a join to a subquery - mapper(Parent, parent_table, properties={ - 'children': relationship(Child, lazy='subquery') - }) + id = Column(Integer, primary_key=True) + children = relationship("Child", lazy='subquery') When querying, all three choices of loader strategy are available on a per-query basis, using the :func:`~sqlalchemy.orm.joinedload`, @@ -117,42 +120,37 @@ query options: # 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(joinedload('foo.bar.bat')).all() - -When using dot-separated names with :func:`~sqlalchemy.orm.joinedload` or -:func:`~sqlalchemy.orm.subqueryload`, the 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(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 joinedloads for both: - -.. sourcecode:: python+sql - - session.query(A).options(joinedload('atob'), joinedload('atob.btoc')).all() - -or more succinctly just use :func:`~sqlalchemy.orm.joinedload_all` or -:func:`~sqlalchemy.orm.subqueryload_all`: - -.. sourcecode:: python+sql - - 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`. +Loading Along Paths +------------------- + +To reference a relationship that is deeper than one level, method chaining +may be used. The object returned by all loader options is an instance of +the :class:`.Load` class, which provides a so-called "generative" interface:: + + session.query(Parent).options( + joinedload('foo'). + joinedload('bar'). + joinedload('bat') + ).all() + +Using method chaining, the loader style of each link in the path is explicitly +stated. To navigate along a path without changing the existing loader style +of a particular attribute, the :func:`.defaultload` method/function may be used:: + + session.query(A).options( + defaultload("atob").joinedload("btoc") + ).all() + +.. versionchanged:: 0.9.0 + The previous approach of specifying dot-separated paths within loader + options has been superseded by the less ambiguous approach of the + :class:`.Load` object and related methods. With this system, the user + specifies the style of loading for each link along the chain explicitly, + rather than guessing between options like ``joinedload()`` vs. ``joinedload_all()``. + The :func:`.orm.defaultload` is provided to allow path navigation without + modification of existing loader options. The dot-separated path system + as well as the ``_all()`` functions will remain available for backwards- + compatibility indefinitely. Default Loading Strategies -------------------------- @@ -175,8 +173,9 @@ of all :func:`.relationship` constructs in use for that query, except for those which use the ``'dynamic'`` style of loading. If some relationships specify ``lazy='joined'`` or ``lazy='subquery'``, for example, -using ``default_strategy(lazy='select')`` will unilaterally -cause all those relationships to use ``'select'`` loading. +using ``lazyload('*')`` will unilaterally +cause all those relationships to use ``'select'`` loading, e.g. emit a +SELECT statement when each attribute is accessed. The option does not supercede loader options stated in the query, such as :func:`.eagerload`, @@ -191,6 +190,22 @@ for the ``widget`` relationship:: If multiple ``'*'`` options are passed, the last one overrides those previously passed. +Per-Entity Default Loading Strategies +------------------------------------- + +.. versionadded:: 0.9.0 + Per-entity default loader strategies. + +A variant of the default loader strategy is the ability to set the strategy +on a per-entity basis. For example, if querying for ``User`` and ``Address``, +we can instruct all relationships on ``Address`` only to use lazy loading +by first applying the :class:`.Load` object, then specifying the ``*`` as a +chained option:: + + session.query(User, Address).options(Load(Address).lazyload('*')) + +Above, all relationships on ``Address`` will be set to a lazy load. + .. _zen_of_eager_loading: The Zen of Eager Loading @@ -402,31 +417,27 @@ 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:: +explicitly. Below, we specify a join between ``User`` and ``Address`` +and addtionally establish this as the basis for eager loading of ``User.addresses``:: - # mapping is the users->addresses mapping - mapper(User, users_table, properties={ - 'addresses': relationship(Address, addresses_table) - }) + class User(Base): + __tablename__ = 'user' + id = Column(Integer, primary_key=True) + addresses = relationship("Address") - # define a query on USERS with an outer join to ADDRESSES - statement = users_table.outerjoin(addresses_table).select().apply_labels() + class Address(Base): + __tablename__ = 'address' - # construct a Query object which expects the "addresses" results - query = session.query(User).options(contains_eager('addresses')) - - # get results normally - r = query.from_statement(statement) + # ... -It works just as well with an inline :meth:`.Query.join` or -:meth:`.Query.outerjoin`:: + q = session.query(User).join(User.addresses).\ + options(contains_eager(User.addresses)) - session.query(User).outerjoin(User.addresses).options(contains_eager(User.addresses)).all() If the "eager" portion of the statement is "aliased", the ``alias`` keyword argument to :func:`~sqlalchemy.orm.contains_eager` may be used to indicate it. -This is a string alias name or reference to an actual -:class:`~sqlalchemy.sql.expression.Alias` (or other selectable) object: +This is sent as a reference to an :func:`.aliased` or :class:`.Alias` +construct: .. sourcecode:: python+sql @@ -444,10 +455,23 @@ This is a string alias name or reference to an actual adalias.user_id AS adalias_user_id, adalias.email_address AS adalias_email_address, (...other columns...) FROM users LEFT OUTER JOIN email_addresses AS email_addresses_1 ON users.user_id = email_addresses_1.user_id -The ``alias`` argument is used only as a source of columns to match up to the -result set. You can use it to match up the result to arbitrary label -names in a string SQL statement, by passing a :func:`.select` which links those -labels to the mapped :class:`.Table`:: +The path given as the argument to :func:`.contains_eager` needs +to be a full path from the starting entity. For example if we were loading +``Users->orders->Order->items->Item``, the string version would look like:: + + query(User).options(contains_eager('orders').contains_eager('items')) + +Or using the class-bound descriptor:: + + query(User).options(contains_eager(User.orders).contains_eager(Order.items)) + +Advanced Usage with Arbitrary Statements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``alias`` argument can be more creatively used, in that it can be made +to represent any set of arbitrary names to match up into a statement. +Below it is linked to a :func:`.select` which links a set of column objects +to a string SQL statement:: # label the columns of the addresses table eager_columns = select([ @@ -463,24 +487,17 @@ labels to the mapped :class:`.Table`:: "from users left outer join addresses on users.user_id=addresses.user_id").\ options(contains_eager(User.addresses, alias=eager_columns)) -The path given as the argument to :func:`.contains_eager` needs -to be a full path from the starting entity. For example if we were loading -``Users->orders->Order->items->Item``, the string version would look like:: - query(User).options(contains_eager('orders', 'items')) - -Or using the class-bound descriptor:: - - query(User).options(contains_eager(User.orders, Order.items)) - -Relation Loader API --------------------- +Relationship Loader API +------------------------ .. autofunction:: contains_alias .. autofunction:: contains_eager +.. autofunction:: defaultload + .. autofunction:: eagerload .. autofunction:: eagerload_all |