summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/query.py84
-rw-r--r--lib/sqlalchemy/orm/strategies.py7
-rw-r--r--lib/sqlalchemy/orm/util.py108
-rw-r--r--lib/sqlalchemy/sql/expression.py2
-rw-r--r--lib/sqlalchemy/sql/util.py2
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))