summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xlib/sqlalchemy/ext/declarative.py6
-rw-r--r--lib/sqlalchemy/orm/relationships.py130
-rw-r--r--lib/sqlalchemy/orm/strategies.py49
-rw-r--r--test/ext/test_declarative.py22
4 files changed, 96 insertions, 111 deletions
diff --git a/lib/sqlalchemy/ext/declarative.py b/lib/sqlalchemy/ext/declarative.py
index 891130a48..7e5ae03d9 100755
--- a/lib/sqlalchemy/ext/declarative.py
+++ b/lib/sqlalchemy/ext/declarative.py
@@ -1392,6 +1392,10 @@ class _GetTable(object):
def _deferred_relationship(cls, prop):
def resolve_arg(arg):
import sqlalchemy
+ from sqlalchemy.orm import foreign, remote
+
+ fallback = sqlalchemy.__dict__.copy()
+ fallback.update({'foreign':foreign, 'remote':remote})
def access_cls(key):
if key in cls._decl_class_registry:
@@ -1401,7 +1405,7 @@ def _deferred_relationship(cls, prop):
elif key in cls.metadata._schemas:
return _GetTable(key, cls.metadata)
else:
- return sqlalchemy.__dict__[key]
+ return fallback[key]
d = util.PopulateDict(access_cls)
def return_cls():
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index adf3b0cde..c1503a79a 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -163,38 +163,42 @@ class JoinCondition(object):
"condition between parent/child tables on "
"relationship %s - there are no foreign keys "
"linking these tables via secondary table '%s'. "
- "Ensure that referencing columns are associated with a "\
- "ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' "\
- "and 'secondaryjoin' expressions."
+ "Ensure that referencing columns are associated "
+ "with a ForeignKey or ForeignKeyConstraint, or "
+ "specify 'primaryjoin' and 'secondaryjoin' "
+ "expressions."
% (self.prop, self.secondary))
else:
raise sa_exc.NoForeignKeysError("Could not determine join "
"condition between parent/child tables on "
"relationship %s - there are no foreign keys "
"linking these tables. "
- "Ensure that referencing columns are associated with a "
- "ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' "
- "expression."
+ "Ensure that referencing columns are associated "
+ "with a ForeignKey or ForeignKeyConstraint, or "
+ "specify a 'primaryjoin' expression."
% self.prop)
except sa_exc.AmbiguousForeignKeysError, afke:
if self.secondary is not None:
- raise sa_exc.AmbiguousForeignKeysError("Could not determine join "
+ raise sa_exc.AmbiguousForeignKeysError(
+ "Could not determine join "
"condition between parent/child tables on "
"relationship %s - there are multiple foreign key "
"paths linking the tables via secondary table '%s'. "
"Specify the 'foreign_keys' "
"argument, providing a list of those columns which "
- "should be counted as containing a foreign key reference "
- "from the secondary table to each of the parent and child tables."
+ "should be counted as containing a foreign key "
+ "reference from the secondary table to each of the "
+ "parent and child tables."
% (self.prop, self.secondary))
else:
- raise sa_exc.AmbiguousForeignKeysError("Could not determine join "
+ raise sa_exc.AmbiguousForeignKeysError(
+ "Could not determine join "
"condition between parent/child tables on "
"relationship %s - there are multiple foreign key "
- "paths linking the tables. Specify the 'foreign_keys' "
- "argument, providing a list of those columns which "
- "should be counted as containing a foreign key reference "
- "to the parent table."
+ "paths linking the tables. Specify the "
+ "'foreign_keys' argument, providing a list of those "
+ "columns which should be counted as containing a "
+ "foreign key reference to the parent table."
% self.prop)
@util.memoized_property
@@ -690,13 +694,13 @@ class JoinCondition(object):
def _gather_join_annotations(self, annotation):
s = set(
- self._gather_columns_with_annotation(self.primaryjoin,
- annotation)
+ self._gather_columns_with_annotation(
+ self.primaryjoin, annotation)
)
if self.secondaryjoin is not None:
s.update(
- self._gather_columns_with_annotation(self.secondaryjoin,
- annotation)
+ self._gather_columns_with_annotation(
+ self.secondaryjoin, annotation)
)
return s
@@ -735,8 +739,8 @@ class JoinCondition(object):
# adjust the join condition for single table inheritance,
# in the case that the join is to a subclass
- # this is analogous to the "_adjust_for_single_table_inheritance()"
- # method in Query.
+ # this is analogous to the
+ # "_adjust_for_single_table_inheritance()" method in Query.
if single_crit is not None:
if secondaryjoin is not None:
@@ -778,51 +782,49 @@ class JoinCondition(object):
return primaryjoin, secondaryjoin, secondary, \
target_adapter, dest_selectable
+ def create_lazy_clause(self, reverse_direction=False):
+ binds = util.column_dict()
+ lookup = util.column_dict()
+ equated_columns = util.column_dict()
+
+ if reverse_direction and self.secondaryjoin is None:
+ for l, r in self.local_remote_pairs:
+ _list = lookup.setdefault(r, [])
+ _list.append((r, l))
+ equated_columns[l] = r
+ else:
+ for l, r in self.local_remote_pairs:
+ _list = lookup.setdefault(l, [])
+ _list.append((l, r))
+ equated_columns[r] = l
+
+ def col_to_bind(col):
+ if col in lookup:
+ for tobind, equated in lookup[col]:
+ if equated in binds:
+ return None
+ if col not in binds:
+ binds[col] = sql.bindparam(
+ None, None, type_=col.type, unique=True)
+ return binds[col]
+ return None
+
+ lazywhere = self.primaryjoin
+
+ if self.secondaryjoin is None or not reverse_direction:
+ lazywhere = visitors.replacement_traverse(
+ lazywhere, {}, col_to_bind)
+
+ if self.secondaryjoin is not None:
+ secondaryjoin = self.secondaryjoin
+ if reverse_direction:
+ secondaryjoin = visitors.replacement_traverse(
+ secondaryjoin, {}, col_to_bind)
+ lazywhere = sql.and_(lazywhere, secondaryjoin)
+
+ bind_to_col = dict((binds[col].key, col) for col in binds)
-################# everything below is TODO ################################
-
-def _create_lazy_clause(cls, prop, reverse_direction=False):
- binds = util.column_dict()
- lookup = util.column_dict()
- equated_columns = util.column_dict()
-
- if reverse_direction and prop.secondaryjoin is None:
- for l, r in prop.local_remote_pairs:
- _list = lookup.setdefault(r, [])
- _list.append((r, l))
- equated_columns[l] = r
- else:
- for l, r in prop.local_remote_pairs:
- _list = lookup.setdefault(l, [])
- _list.append((l, r))
- equated_columns[r] = l
-
- def col_to_bind(col):
- if col in lookup:
- for tobind, equated in lookup[col]:
- if equated in binds:
- return None
- if col not in binds:
- binds[col] = sql.bindparam(None, None, type_=col.type, unique=True)
- return binds[col]
- return None
-
- lazywhere = prop.primaryjoin
-
- if prop.secondaryjoin is None or not reverse_direction:
- lazywhere = visitors.replacement_traverse(
- lazywhere, {}, col_to_bind)
-
- if prop.secondaryjoin is not None:
- secondaryjoin = prop.secondaryjoin
- if reverse_direction:
- secondaryjoin = visitors.replacement_traverse(
- secondaryjoin, {}, col_to_bind)
- lazywhere = sql.and_(lazywhere, secondaryjoin)
-
- bind_to_col = dict((binds[col].key, col) for col in binds)
-
- return lazywhere, bind_to_col, equated_columns
+ return lazywhere, bind_to_col, equated_columns
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 00739ea03..a4cdfba1a 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -324,14 +324,14 @@ class LazyLoader(AbstractRelationshipLoader):
def init(self):
super(LazyLoader, self).init()
+ join_condition = self.parent_property._join_condition
self._lazywhere, \
self._bind_to_col, \
- self._equated_columns = self._create_lazy_clause(self.parent_property)
+ self._equated_columns = join_condition.create_lazy_clause()
self._rev_lazywhere, \
self._rev_bind_to_col, \
- self._rev_equated_columns = self._create_lazy_clause(
- self.parent_property,
+ self._rev_equated_columns = join_condition.create_lazy_clause(
reverse_direction=True)
self.logger.info("%s lazy loading clause %s", self, self._lazywhere)
@@ -617,49 +617,6 @@ class LazyLoader(AbstractRelationshipLoader):
return reset_for_lazy_callable, None, None
- @classmethod
- def _create_lazy_clause(cls, prop, reverse_direction=False):
- binds = util.column_dict()
- lookup = util.column_dict()
- equated_columns = util.column_dict()
-
- if reverse_direction and prop.secondaryjoin is None:
- for l, r in prop.local_remote_pairs:
- _list = lookup.setdefault(r, [])
- _list.append((r, l))
- equated_columns[l] = r
- else:
- for l, r in prop.local_remote_pairs:
- _list = lookup.setdefault(l, [])
- _list.append((l, r))
- equated_columns[r] = l
-
- def col_to_bind(col):
- if col in lookup:
- for tobind, equated in lookup[col]:
- if equated in binds:
- return None
- if col not in binds:
- binds[col] = sql.bindparam(None, None, type_=col.type, unique=True)
- return binds[col]
- return None
-
- lazywhere = prop.primaryjoin
-
- if prop.secondaryjoin is None or not reverse_direction:
- lazywhere = visitors.replacement_traverse(
- lazywhere, {}, col_to_bind)
-
- if prop.secondaryjoin is not None:
- secondaryjoin = prop.secondaryjoin
- if reverse_direction:
- secondaryjoin = visitors.replacement_traverse(
- secondaryjoin, {}, col_to_bind)
- lazywhere = sql.and_(lazywhere, secondaryjoin)
-
- bind_to_col = dict((binds[col].key, col) for col in binds)
-
- return lazywhere, bind_to_col, equated_columns
log.class_logger(LazyLoader)
diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py
index 69042b5c8..5e185f664 100644
--- a/test/ext/test_declarative.py
+++ b/test/ext/test_declarative.py
@@ -368,6 +368,28 @@ class DeclarativeTest(DeclarativeTestBase):
assert class_mapper(User).get_property('props').secondary \
is user_to_prop
+ def test_string_dependency_resolution_annotations(self):
+ Base = decl.declarative_base()
+
+ class Parent(Base):
+ __tablename__ = 'parent'
+ id = Column(Integer, primary_key=True)
+ name = Column(String)
+ children = relationship("Child",
+ primaryjoin="Parent.name==remote(foreign(func.lower(Child.name_upper)))"
+ )
+
+ class Child(Base):
+ __tablename__ = 'child'
+ id = Column(Integer, primary_key=True)
+ name_upper = Column(String)
+
+ configure_mappers()
+ eq_(
+ Parent.children.property._calculated_foreign_keys,
+ set([Child.name_upper.property.columns[0]])
+ )
+
def test_shared_class_registry(self):
reg = {}
Base1 = decl.declarative_base(testing.db, class_registry=reg)