diff options
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 84 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/util.py | 108 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/expression.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/util.py | 2 |
5 files changed, 82 insertions, 121 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index b18e28abb..c9f3a2699 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1812,9 +1812,9 @@ class Query(object): "are the same entity" % (left, right)) - right, right_is_aliased, onclause = self._prepare_right_side( + right, onclause = self._prepare_right_side( right, onclause, - outerjoin, create_aliases, + create_aliases, prop) # if joining on a MapperProperty path, @@ -1825,16 +1825,11 @@ class Query(object): 'prev': ((left, right, prop.key), self._joinpoint) }) else: - self._joinpoint = { - '_joinpoint_entity': right - } + self._joinpoint = {'_joinpoint_entity': right} - self._join_to_left(left, right, - right_is_aliased, - onclause, outerjoin) + self._join_to_left(left, right, onclause, outerjoin) - def _prepare_right_side(self, right, onclause, outerjoin, - create_aliases, prop): + def _prepare_right_side(self, right, onclause, create_aliases, prop): info = inspect(right) right_mapper, right_selectable, right_is_aliased = \ @@ -1911,48 +1906,22 @@ class Query(object): ) ) - return right, right_is_aliased, onclause + return right, onclause - def _join_to_left(self, left, right, right_is_aliased, - onclause, outerjoin): + def _join_to_left(self, left, right, onclause, outerjoin): info = inspect(left) - left_mapper, left_selectable, left_is_aliased = \ - getattr(info, 'mapper', None),\ - info.selectable,\ - getattr(info, 'is_aliased_class', False) + left_mapper = getattr(info, 'mapper', None) + left_selectable = info.selectable - # this is an overly broad assumption here, but there's a - # very wide variety of situations where we rely upon orm.join's - # adaption to glue clauses together, with joined-table inheritance's - # wide array of variables taking up most of the space. - # Setting the flag here is still a guess, so it is a bug - # that we don't have definitive criterion to determine when - # adaption should be enabled (or perhaps that we're even doing the - # whole thing the way we are here). - join_to_left = not right_is_aliased and not left_is_aliased - - if self._from_obj and left_selectable is not None: + if self._from_obj: replace_clause_index, clause = sql_util.find_join_source( self._from_obj, left_selectable) if clause is not None: - # the entire query's FROM clause is an alias of itself (i.e. - # from_self(), similar). if the left clause is that one, - # ensure it adapts to the left side. - if self._from_obj_alias and clause is self._from_obj[0]: - join_to_left = True - - # An exception case where adaption to the left edge is not - # desirable. See above note on join_to_left. - if join_to_left and isinstance(clause, expression.Join) and \ - sql_util.clause_is_present(left_selectable, clause): - join_to_left = False - try: clause = orm_join(clause, right, - onclause, isouter=outerjoin, - join_to_left=join_to_left) + onclause, isouter=outerjoin) except sa_exc.ArgumentError, ae: raise sa_exc.InvalidRequestError( "Could not find a FROM clause to join from. " @@ -1971,18 +1940,13 @@ class Query(object): break else: clause = left - elif left_selectable is not None: - clause = left_selectable else: - clause = None + clause = left_selectable - if clause is None: - raise sa_exc.InvalidRequestError( - "Could not find a FROM clause to join from") + assert clause is not None try: - clause = orm_join(clause, right, onclause, - isouter=outerjoin, join_to_left=join_to_left) + clause = orm_join(clause, right, onclause, isouter=outerjoin) except sa_exc.ArgumentError, ae: raise sa_exc.InvalidRequestError( "Could not find a FROM clause to join from. " @@ -2373,6 +2337,26 @@ class Query(object): kwargs.get('offset') is not None or kwargs.get('distinct', False)) + def exists(self): + """A convenience method that turns a query into an EXISTS subquery + of the form EXISTS (SELECT 1 FROM ... WHERE ...). + + e.g.:: + + q = session.query(User).filter(User.name == 'fred') + session.query(q.exists()) + + Producing SQL similar to:: + + SELECT EXISTS ( + SELECT 1 FROM users WHERE users.name = :name_1 + ) AS anon_1 + + .. versionadded:: 0.8.1 + + """ + return sql.exists(self.with_entities('1').statement) + def count(self): """Return a count of rows this Query would return. diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 0eed50ea4..6660a39ef 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -1152,7 +1152,6 @@ class JoinedLoader(AbstractRelationshipLoader): towrap = context.eager_joins.setdefault(entity_key, default_towrap) - join_to_left = False if adapter: if getattr(adapter, 'aliased_class', None): onclause = getattr( @@ -1168,11 +1167,6 @@ class JoinedLoader(AbstractRelationshipLoader): self.key, self.parent_property ) - if onclause is self.parent_property: - # TODO: this is a temporary hack to - # account for polymorphic eager loads where - # the eagerload is referencing via of_type(). - join_to_left = True else: onclause = self.parent_property @@ -1182,7 +1176,6 @@ class JoinedLoader(AbstractRelationshipLoader): towrap, clauses.aliased_class, onclause, - join_to_left=join_to_left, isouter=not innerjoin ) diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index f3b8e271d..35cb0bdf5 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -870,73 +870,59 @@ class _ORMJoin(expression.Join): __visit_name__ = expression.Join.__visit_name__ - def __init__(self, left, right, onclause=None, - isouter=False, join_to_left=True): - adapt_from = None - - if hasattr(left, '_orm_mappers'): - left_mapper = left._orm_mappers[1] - if join_to_left: - adapt_from = left.right - else: - info = inspection.inspect(left) - left_mapper = getattr(info, 'mapper', None) - left = info.selectable - left_is_aliased = getattr(info, 'is_aliased_class', False) + def __init__(self, left, right, onclause=None, isouter=False): + + left_info = inspection.inspect(left) + left_orm_info = getattr(left, '_joined_from_info', left_info) - if join_to_left and (left_is_aliased or not left_mapper): - adapt_from = left + right_info = inspection.inspect(right) + adapt_to = right_info.selectable - info = inspection.inspect(right) - right_mapper = getattr(info, 'mapper', None) - right = info.selectable - right_is_aliased = getattr(info, 'is_aliased_class', False) + self._joined_from_info = right_info - if right_is_aliased: - adapt_to = right + if isinstance(onclause, basestring): + onclause = getattr(left_orm_info.entity, onclause) + + if isinstance(onclause, attributes.QueryableAttribute): + on_selectable = onclause.comparator._source_selectable() + prop = onclause.property + elif isinstance(onclause, MapperProperty): + prop = onclause + on_selectable = prop.parent.selectable else: - adapt_to = None - - if left_mapper or right_mapper: - self._orm_mappers = (left_mapper, right_mapper) - - if isinstance(onclause, basestring): - prop = left_mapper.get_property(onclause) - elif isinstance(onclause, attributes.QueryableAttribute): - if adapt_from is None: - adapt_from = onclause.comparator._source_selectable() - prop = onclause.property - elif isinstance(onclause, MapperProperty): - prop = onclause + prop = None + + if prop: + if sql_util.clause_is_present(on_selectable, left_info.selectable): + adapt_from = on_selectable else: - prop = None + adapt_from = left_info.selectable - if prop: - pj, sj, source, dest, \ + pj, sj, source, dest, \ secondary, target_adapter = prop._create_joins( - source_selectable=adapt_from, - dest_selectable=adapt_to, - source_polymorphic=True, - dest_polymorphic=True, - of_type=right_mapper) - - if sj is not None: - left = sql.join(left, secondary, pj, isouter) - onclause = sj - else: - onclause = pj - self._target_adapter = target_adapter + source_selectable=adapt_from, + dest_selectable=adapt_to, + source_polymorphic=True, + dest_polymorphic=True, + of_type=right_info.mapper) + + if sj is not None: + left = sql.join(left, secondary, pj, isouter) + onclause = sj + else: + onclause = pj + self._target_adapter = target_adapter expression.Join.__init__(self, left, right, onclause, isouter) - def join(self, right, onclause=None, isouter=False, join_to_left=True): - return _ORMJoin(self, right, onclause, isouter, join_to_left) + def join(self, right, onclause=None, isouter=False, join_to_left=None): + return _ORMJoin(self, right, onclause, isouter) - def outerjoin(self, right, onclause=None, join_to_left=True): - return _ORMJoin(self, right, onclause, True, join_to_left) + def outerjoin(self, right, onclause=None, join_to_left=None): + return _ORMJoin(self, right, onclause, True) -def join(left, right, onclause=None, isouter=False, join_to_left=True): +def join(left, right, onclause=None, isouter=False, join_to_left=None): """Produce an inner join between left and right clauses. :func:`.orm.join` is an extension to the core join interface @@ -947,11 +933,6 @@ def join(left, right, onclause=None, isouter=False, join_to_left=True): be a SQL expression, or an attribute or string name referencing a configured :func:`.relationship`. - ``join_to_left`` indicates to attempt aliasing the ON clause, - in whatever form it is passed, to the selectable - passed as the left side. If False, the onclause - is used as is. - :func:`.orm.join` is not commonly needed in modern usage, as its functionality is encapsulated within that of the :meth:`.Query.join` method, which features a @@ -975,11 +956,14 @@ def join(left, right, onclause=None, isouter=False, join_to_left=True): See :meth:`.Query.join` for information on modern usage of ORM level joins. + .. versionchanged:: 0.8.1 - the ``join_to_left`` parameter + is no longer used, and is deprecated. + """ - return _ORMJoin(left, right, onclause, isouter, join_to_left) + return _ORMJoin(left, right, onclause, isouter) -def outerjoin(left, right, onclause=None, join_to_left=True): +def outerjoin(left, right, onclause=None, join_to_left=None): """Produce a left outer join between left and right clauses. This is the "outer join" version of the :func:`.orm.join` function, @@ -987,7 +971,7 @@ def outerjoin(left, right, onclause=None, join_to_left=True): See that function's documentation for other usage details. """ - return _ORMJoin(left, right, onclause, True, join_to_left) + return _ORMJoin(left, right, onclause, True) def with_parent(instance, prop): diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index d2e644ce2..7846ac3b2 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -3909,7 +3909,7 @@ class Join(FromClause): def is_derived_from(self, fromclause): return fromclause is self or \ - self.left.is_derived_from(fromclause) or\ + self.left.is_derived_from(fromclause) or \ self.right.is_derived_from(fromclause) def self_group(self, against=None): diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 520c90f99..4aa2d7496 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -203,7 +203,7 @@ def clause_is_present(clause, search): stack = [search] while stack: elem = stack.pop() - if clause is elem: + if clause == elem: # use == here so that Annotated's compare return True elif isinstance(elem, expression.Join): stack.extend((elem.left, elem.right)) |