summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES5
-rw-r--r--lib/sqlalchemy/orm/strategies.py25
-rw-r--r--test/orm/query.py21
3 files changed, 47 insertions, 4 deletions
diff --git a/CHANGES b/CHANGES
index 8ec857dad..35d53ab61 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,11 @@ CHANGES
- Also re-established viewonly relation() configurations
that join across multiple tables.
+ - contains_eager(), the hot function of the week, suppresses
+ the eager loader's own generation of the LEFT OUTER JOIN,
+ so that it is reasonable to use any Query, not just those
+ which use from_statement().
+
- Added an experimental relation() flag to help with
primaryjoins across functions, etc.,
_local_remote_pairs=[tuples]. This complements a complex
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index e758ac08b..65a8b019b 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -502,6 +502,28 @@ class EagerLoader(AbstractRelationLoader):
if self.mapper.base_mapper in path:
return
+ if ("eager_row_processor", path) in context.attributes:
+ # if user defined eager_row_processor, that's contains_eager().
+ # don't render LEFT OUTER JOIN, generate an AliasedClauses from
+ # the decorator (this is a hack here, cleaned up in 0.5)
+ cl = context.attributes[("eager_row_processor", path)]
+ if cl:
+ row = cl(None)
+ class ActsLikeAliasedClauses(object):
+ def aliased_column(self, col):
+ return row.map[col]
+ clauses = ActsLikeAliasedClauses()
+ else:
+ clauses = None
+ else:
+ clauses = self.__create_eager_join(context, path, parentclauses, parentmapper, **kwargs)
+ if not clauses:
+ return
+
+ for value in self.mapper._iterate_polymorphic_properties():
+ context.exec_with_path(self.mapper, value.key, value.setup, context, parentclauses=clauses, parentmapper=self.mapper)
+
+ def __create_eager_join(self, context, path, parentclauses, parentmapper, **kwargs):
if parentmapper is None:
localparent = context.mapper
else:
@@ -546,8 +568,7 @@ class EagerLoader(AbstractRelationLoader):
if clauses.order_by:
context.eager_order_by += util.to_list(clauses.order_by)
- for value in self.mapper._iterate_polymorphic_properties():
- context.exec_with_path(self.mapper, value.key, value.setup, context, parentclauses=clauses, parentmapper=self.mapper)
+ return clauses
def _create_row_decorator(self, selectcontext, row, path):
"""Create a *row decorating* function that will apply eager
diff --git a/test/orm/query.py b/test/orm/query.py
index cdd50cb70..f1afdb90b 100644
--- a/test/orm/query.py
+++ b/test/orm/query.py
@@ -906,7 +906,7 @@ class SynonymTest(QueryTest):
o = sess.query(Order).with_parent(u1, property=orderprop).all()
assert [Order(description="order 1"), Order(description="order 3"), Order(description="order 5")] == o
-class InstancesTest(QueryTest):
+class InstancesTest(QueryTest, AssertsCompiledSQL):
def test_from_alias(self):
@@ -927,9 +927,26 @@ class InstancesTest(QueryTest):
self.assert_sql_count(testing.db, go, 1)
def test_contains_eager(self):
+ sess = create_session()
+ q = sess.query(User).outerjoin(User.addresses).options(contains_eager(User.addresses))
+ self.assert_compile(q.statement, "SELECT addresses.id AS addresses_id, addresses.user_id AS addresses_user_id, "
+ "addresses.email_address AS addresses_email_address, users.id AS users_id, users.name AS users_name "\
+ "FROM users LEFT OUTER JOIN addresses ON users.id = addresses.user_id ORDER BY users.id", dialect=default.DefaultDialect())
+
+ def go():
+ assert fixtures.user_address_result == q.all()
+ self.assert_sql_count(testing.db, go, 1)
+ sess.clear()
+
+ adalias = addresses.alias()
+ q = sess.query(User).select_from(users.outerjoin(adalias)).options(contains_eager(User.addresses, alias=adalias))
+ def go():
+ assert fixtures.user_address_result == q.all()
+ self.assert_sql_count(testing.db, go, 1)
+ sess.clear()
+
selectquery = users.outerjoin(addresses).select(users.c.id<10, use_labels=True, order_by=[users.c.id, addresses.c.id])
- sess = create_session()
q = sess.query(User)
def go():