summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-11-19 15:24:22 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2015-11-19 15:24:22 -0500
commit1dc805dd4d902b9204703f0bd6151c58f1f287af (patch)
tree6945642c0e794fe3ba5f7bf6475c09513ba95c7a
parent9ed36c2eed34b23560abe3cdf373c720b24d3b30 (diff)
downloadsqlalchemy-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.rst44
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^