summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-01-23 19:20:49 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-01-23 19:20:49 +0000
commitc8b50c1ffb4ab104144ef9058fa4c653b10ad915 (patch)
treebf59a61d9c5a1ba4f6c1ef1fb2342ad140b63885 /lib/sqlalchemy
parentf980c5c88f616dddf51089dd37ee1023400c0a41 (diff)
downloadsqlalchemy-c8b50c1ffb4ab104144ef9058fa4c653b10ad915.tar.gz
- query.join() can also accept tuples of attribute
name/some selectable as arguments. This allows construction of joins *from* subclasses of a polymorphic relation, i.e.: query(Company).\ join( [('employees', people.join(engineer)), Engineer.name] )
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/query.py60
-rw-r--r--lib/sqlalchemy/orm/util.py4
-rw-r--r--lib/sqlalchemy/sql/expression.py3
3 files changed, 52 insertions, 15 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 201d0e2e3..bcf0f3dd3 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -422,18 +422,39 @@ class Query(object):
mapper = start
alias = self._aliases
- for key in util.to_list(keys):
+ if not isinstance(keys, list):
+ keys = [keys]
+ for key in keys:
+ use_selectable = None
+ if isinstance(key, tuple):
+ key, use_selectable = key
+
if isinstance(key, interfaces.PropComparator):
prop = key.property
else:
prop = mapper.get_property(key, resolve_synonyms=True)
+
+ if use_selectable:
+ if not use_selectable.is_derived_from(prop.mapper.mapped_table):
+ raise exceptions.InvalidRequestError("Selectable '%s' is not derived from '%s'" % (use_selectable.description, prop.mapper.mapped_table.description))
+ if not isinstance(use_selectable, expression.Alias):
+ use_selectable = use_selectable.alias()
- if prop._is_self_referential() and not create_aliases:
+ if prop._is_self_referential() and not create_aliases and not use_selectable:
raise exceptions.InvalidRequestError("Self-referential query on '%s' property requires create_aliases=True argument." % str(prop))
- if prop.select_table not in currenttables or create_aliases:
+ if prop.select_table not in currenttables or create_aliases or use_selectable:
if prop.secondary:
- if create_aliases:
+ if use_selectable:
+ alias = mapperutil.PropertyAliasedClauses(prop,
+ prop.primary_join_against(mapper, adapt_against),
+ prop.secondary_join_against(mapper),
+ alias,
+ alias=use_selectable
+ )
+ crit = alias.primaryjoin
+ clause = clause.join(alias.secondary, crit, isouter=outerjoin).join(alias.alias, alias.secondaryjoin, isouter=outerjoin)
+ elif create_aliases:
alias = mapperutil.PropertyAliasedClauses(prop,
prop.primary_join_against(mapper, adapt_against),
prop.secondary_join_against(mapper),
@@ -446,7 +467,16 @@ class Query(object):
clause = clause.join(prop.secondary, crit, isouter=outerjoin)
clause = clause.join(prop.select_table, prop.secondary_join_against(mapper), isouter=outerjoin)
else:
- if create_aliases:
+ if use_selectable:
+ alias = mapperutil.PropertyAliasedClauses(prop,
+ prop.primary_join_against(mapper, adapt_against),
+ None,
+ alias,
+ alias=use_selectable
+ )
+ crit = alias.primaryjoin
+ clause = clause.join(alias.alias, crit, isouter=outerjoin)
+ elif create_aliases:
alias = mapperutil.PropertyAliasedClauses(prop,
prop.primary_join_against(mapper, adapt_against),
None,
@@ -464,13 +494,12 @@ class Query(object):
mapper = prop.mapper
- if mapper.select_table is not mapper.mapped_table:
+ if use_selectable:
+ adapt_against = use_selectable
+ elif mapper.select_table is not mapper.mapped_table:
adapt_against = mapper.select_table
- if create_aliases:
- return (clause, mapper, alias)
- else:
- return (clause, mapper, None)
+ return (clause, mapper, alias)
def _generative_col_aggregate(self, col, func):
"""apply the given aggregate function to the query and return the newly
@@ -594,14 +623,16 @@ class Query(object):
'prop' may be one of:
* a string property name, i.e. "rooms"
* a class-mapped attribute, i.e. Houses.rooms
- * a list containing a combination of any of the above.
+ * a 2-tuple containing one of the above, combined with a selectable
+ which derives from the properties' mapped table
+ * a list (not a tuple) containing a combination of any of the above.
e.g.::
session.query(Company).join('employees')
session.query(Company).join(['employees', 'tasks'])
session.query(Houses).join([Colonials.rooms, Room.closets])
-
+ session.query(Company).join([('employees', people.join(engineers)), Engineer.computers])
"""
return self._join(prop, id=id, outerjoin=False, aliased=aliased, from_joinpoint=from_joinpoint)
@@ -613,13 +644,16 @@ class Query(object):
'prop' may be one of:
* a string property name, i.e. "rooms"
* a class-mapped attribute, i.e. Houses.rooms
- * a list containing a combination of any of the above.
+ * a 2-tuple containing one of the above, combined with a selectable
+ which derives from the properties' mapped table
+ * a list (not a tuple) containing a combination of any of the above.
e.g.::
session.query(Company).outerjoin('employees')
session.query(Company).outerjoin(['employees', 'tasks'])
session.query(Houses).outerjoin([Colonials.rooms, Room.closets])
+ session.query(Company).join([('employees', people.join(engineers)), Engineer.computers])
"""
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index d2f1d2491..a801210f9 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -193,8 +193,8 @@ class AliasedClauses(object):
class PropertyAliasedClauses(AliasedClauses):
"""extends AliasedClauses to add support for primary/secondary joins on a relation()."""
- def __init__(self, prop, primaryjoin, secondaryjoin, parentclauses=None):
- super(PropertyAliasedClauses, self).__init__(prop.select_table)
+ def __init__(self, prop, primaryjoin, secondaryjoin, parentclauses=None, alias=None):
+ super(PropertyAliasedClauses, self).__init__(prop.select_table, alias=alias)
self.parentclauses = parentclauses
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index c60341802..aff8654f2 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -2202,6 +2202,9 @@ class Join(FromClause):
return "Join object on %s(%d) and %s(%d)" % (self.left.description, id(self.left), self.right.description, id(self.right))
description = property(description)
+ def is_derived_from(self, fromclause):
+ return fromclause is self or self.left.is_derived_from(fromclause) or self.right.is_derived_from(fromclause)
+
def self_group(self, against=None):
return _FromGrouping(self)