diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-11-19 15:24:22 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-11-19 15:24:22 -0500 |
commit | 1dc805dd4d902b9204703f0bd6151c58f1f287af (patch) | |
tree | 6945642c0e794fe3ba5f7bf6475c09513ba95c7a | |
parent | 9ed36c2eed34b23560abe3cdf373c720b24d3b30 (diff) | |
download | sqlalchemy-1dc805dd4d902b9204703f0bd6151c58f1f287af.tar.gz |
- add documentation that describes, then proceeds to warn about
the many caveats and confusing effects of, the popular approach
of using contains_eager() to alter the natural result of a
related collection. I'm not a fan of this technique as it changes
the semantics of a relationship in such a way that the rest of the
ORM isn't aware of and it also can be undone very easily; hence the
section needs as much text for warnings as for describing the
technique itself. fixes #3563
-rw-r--r-- | doc/build/orm/loading_relationships.rst | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/doc/build/orm/loading_relationships.rst b/doc/build/orm/loading_relationships.rst index 297392f3e..3a0026bbe 100644 --- a/doc/build/orm/loading_relationships.rst +++ b/doc/build/orm/loading_relationships.rst @@ -494,6 +494,50 @@ Or using the class-bound descriptor:: query(User).options(contains_eager(User.orders).contains_eager(Order.items)) +Using contains_eager() to load a custom-filtered collection result +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When we use :func:`.contains_eager`, *we* are constructing ourselves the +SQL that will be used to populate collections. From this, it naturally follows +that we can opt to **modify** what values the collection is intended to store, +by writing our SQL to load a subset of elements for collections or +scalar attributes. + +As an example, we can load a ``User`` object and eagerly load only particular +addresses into its ``.addresses`` collection just by filtering:: + + q = session.query(User).join(User.addresses).\ + filter(Address.email.like('%ed%')).\ + options(contains_eager(User.addresses)) + +The above query will load only ``User`` objects which contain at +least ``Address`` object that contains the substring ``'ed'`` in its +``email`` field; the ``User.addresses`` collection will contain **only** +these ``Address`` entries, and *not* any other ``Address`` entries that are +in fact associated with the collection. + +.. warning:: + + Keep in mind that when we load only a subset of objects into a collection, + that collection no longer represents what's actually in the database. If + we attempted to add entries to this collection, we might find ourselves + conflicting with entries that are already in the database but not locally + loaded. + + In addition, the **collection will fully reload normally** once the + object or attribute is expired. This expiration occurs whenever the + :meth:`.Session.commit`, :meth:`.Session.rollback` methods are used + assuming default session settings, or the :meth:`.Session.expire_all` + or :meth:`.Session.expire` methods are used. + + For these reasons, prefer returning separate fields in a tuple rather + than artificially altering a collection, when an object plus a custom + set of related objects is desired:: + + q = session.query(User, Address).join(User.addresses).\ + filter(Address.email.like('%ed%')) + + Advanced Usage with Arbitrary Statements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |