self._generate_backref()
super(RelationshipProperty, self).do_init()
+ def _process_dependent_arguments(self):
+ """Convert incoming configuration arguments to their
+ proper form.
+
+ Callables are resolved, ORM annotations removed.
+
+ """
+ # accept callables for other attributes which may require
+ # deferred initialization. This technique is used
+ # by declarative "string configs" and some recipes.
+ for attr in (
+ 'order_by', 'primaryjoin', 'secondaryjoin',
+ 'secondary', '_user_defined_foreign_keys', 'remote_side',
+ ):
+ attr_value = getattr(self, attr)
+ if util.callable(attr_value):
+ setattr(self, attr, attr_value())
+
+ # remove "annotations" which are present if mapped class
+ # descriptors are used to create the join expression.
+ for attr in 'primaryjoin', 'secondaryjoin':
+ val = getattr(self, attr)
+ if val is not None:
+ setattr(self, attr, _orm_deannotate(
+ expression._only_column_elements(val, attr))
+ )
+
+ # ensure expressions in self.order_by, foreign_keys,
+ # remote_side are all columns, not strings.
+ if self.order_by is not False and self.order_by is not None:
+ self.order_by = [
+ expression._only_column_elements(x, "order_by")
+ for x in
+ util.to_list(self.order_by)]
+
+ self._user_defined_foreign_keys = \
+ util.column_set(
+ expression._only_column_elements(x, "foreign_keys")
+ for x in util.to_column_set(
+ self._user_defined_foreign_keys
+ ))
+
+ self.remote_side = \
+ util.column_set(
+ expression._only_column_elements(x, "remote_side")
+ for x in
+ util.to_column_set(self.remote_side))
+
+ self.target = self.mapper.mapped_table
+
+ if self.cascade.delete_orphan:
+ self.mapper.primary_mapper().delete_orphans.append(
+ (self.key, self.parent.class_)
+ )
+
def _setup_join_conditions(self):
self._join_condition = jc = relationships.JoinCondition(
parent_selectable=self.parent.mapped_table,
self.direction = jc.direction
self.local_remote_pairs = jc.local_remote_pairs
self.remote_side = jc.remote_columns
+ self.local_columns = jc.local_columns
self.synchronize_pairs = jc.synchronize_pairs
self._calculated_foreign_keys = jc.foreign_key_columns
self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs
"cause dependency issues during flush"
% (self.key, self.parent, inheriting))
- def _process_dependent_arguments(self):
- """Convert incoming configuration arguments to their
- proper form.
-
- Callables are resolved, ORM annotations removed.
-
- """
- # accept callables for other attributes which may require
- # deferred initialization. This technique is used
- # by declarative "string configs" and some recipes.
- for attr in (
- 'order_by',
- 'primaryjoin',
- 'secondaryjoin',
- 'secondary',
- '_user_defined_foreign_keys',
- 'remote_side',
- ):
- attr_value = getattr(self, attr)
- if util.callable(attr_value):
- setattr(self, attr, attr_value())
-
- # remove "annotations" which are present if mapped class
- # descriptors are used to create the join expression.
- for attr in 'primaryjoin', 'secondaryjoin':
- val = getattr(self, attr)
- if val is not None:
- setattr(self, attr, _orm_deannotate(
- expression._only_column_elements(val, attr))
- )
-
- # ensure expressions in self.order_by, foreign_keys,
- # remote_side are all columns, not strings.
- if self.order_by is not False and self.order_by is not None:
- self.order_by = [
- expression._only_column_elements(x, "order_by")
- for x in
- util.to_list(self.order_by)]
-
- self._user_defined_foreign_keys = \
- util.column_set(
- expression._only_column_elements(x, "foreign_keys")
- for x in util.to_column_set(
- self._user_defined_foreign_keys
- ))
-
- self.remote_side = \
- util.column_set(
- expression._only_column_elements(x, "remote_side")
- for x in
- util.to_column_set(self.remote_side))
-
- self.target = self.mapper.mapped_table
-
- if self.cascade.delete_orphan:
- self.mapper.primary_mapper().delete_orphans.append(
- (self.key, self.parent.class_)
- )
def _check_cascade_settings(self):
if self.cascade.delete_orphan and not self.single_parent \
kwargs.setdefault('passive_updates', self.passive_updates)
self.back_populates = backref_key
relationship = RelationshipProperty(
- parent,
- self.secondary,
- pj,
- sj,
+ parent, self.secondary,
+ pj, sj,
foreign_keys=foreign_keys,
back_populates=self.key,
- **kwargs
- )
+ **kwargs)
mapper._configure_property(backref_key, relationship)
if self.back_populates:
else:
aliased = True
- # place a barrier on the destination such that
- # replacement traversals won't ever dig into it.
- # its internal structure remains fixed
- # regardless of context.
- dest_selectable = _shallow_annotate(
- dest_selectable,
- {'no_replacement_traverse':True})
-
- aliased = aliased or (source_selectable is not None)
-
- primaryjoin, secondaryjoin, secondary = self.primaryjoin, \
- self.secondaryjoin, self.secondary
-
- # 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.
-
dest_mapper = of_type or self.mapper
single_crit = dest_mapper._single_table_criterion
- if single_crit is not None:
- if secondaryjoin is not None:
- secondaryjoin = secondaryjoin & single_crit
- else:
- primaryjoin = primaryjoin & single_crit
-
- if aliased:
- if secondary is not None:
- secondary = secondary.alias()
- primary_aliasizer = ClauseAdapter(secondary)
- secondary_aliasizer = \
- ClauseAdapter(dest_selectable,
- equivalents=self.mapper._equivalent_columns).\
- chain(primary_aliasizer)
- if source_selectable is not None:
- primary_aliasizer = \
- ClauseAdapter(secondary).\
- chain(ClauseAdapter(source_selectable,
- equivalents=self.parent._equivalent_columns))
- secondaryjoin = \
- secondary_aliasizer.traverse(secondaryjoin)
- else:
- primary_aliasizer = ClauseAdapter(dest_selectable,
- #exclude=self.local_side,
- exclude_fn=lambda c: "local" in c._annotations,
- equivalents=self.mapper._equivalent_columns)
- if source_selectable is not None:
- primary_aliasizer.chain(
- ClauseAdapter(source_selectable,
- #exclude=self.remote_side,
- exclude_fn=lambda c: "remote" in c._annotations,
- equivalents=self.parent._equivalent_columns))
- secondary_aliasizer = None
-
- primaryjoin = primary_aliasizer.traverse(primaryjoin)
- target_adapter = secondary_aliasizer or primary_aliasizer
- target_adapter.include = target_adapter.exclude = target_adapter.exclude_fn = None
- else:
- target_adapter = None
+ aliased = aliased or (source_selectable is not None)
+
+ primaryjoin, secondaryjoin, secondary, target_adapter, dest_selectable = \
+ self._join_condition.join_targets(
+ source_selectable, dest_selectable, aliased, single_crit
+ )
if source_selectable is None:
source_selectable = self.parent.local_table
if dest_selectable is None:
dest_selectable = self.mapper.local_table
- return (
- primaryjoin,
- secondaryjoin,
- source_selectable,
- dest_selectable,
- secondary,
- target_adapter,
- )
+ return (primaryjoin, secondaryjoin, source_selectable,
+ dest_selectable, secondary, target_adapter)
+
PropertyLoader = RelationProperty = RelationshipProperty
log.class_logger(RelationshipProperty)
leftmost_mapper, leftmost_prop = \
subq_mapper, \
subq_mapper._props[subq_path[1]]
- # TODO: local cols might not be unique here
- leftmost_cols, remote_cols = self._local_remote_columns(leftmost_prop)
+
+ leftmost_cols = leftmost_prop.local_columns
leftmost_attr = [
leftmost_mapper._columntoproperty[c].class_attribute
# self.parent is more specific than subq_path[-2]
parent_alias = mapperutil.AliasedClass(self.parent)
- # TODO: local cols might not be unique here
- local_cols, remote_cols = \
- self._local_remote_columns(self.parent_property)
+ local_cols = self.parent_property.local_columns
local_attr = [
getattr(parent_alias, self.parent._columntoproperty[c].key)
q = q.join(attr, aliased=middle, from_joinpoint=True)
return q
- def _local_remote_columns(self, prop):
- if prop.secondary is None:
- return zip(*prop.local_remote_pairs)
- else:
- # TODO: this isn't going to work for readonly....
- return \
- [p[0] for p in prop.synchronize_pairs],\
- [
- p[0] for p in prop.
- secondary_synchronize_pairs
- ]
-
def _setup_options(self, q, subq_path, orig_query):
# propagate loader options etc. to the new query.
# these will fire relative to subq_path.
if ('subquery', reduced_path) not in context.attributes:
return None, None, None
- # TODO: local_cols might not be unique here
- local_cols, remote_cols = self._local_remote_columns(self.parent_property)
+ local_cols = self.parent_property.local_columns
q = context.attributes[('subquery', reduced_path)]
def test_join_targets_o2m_selfref(self):
joincond = self._join_fixture_o2m_selfref()
left = select([joincond.parent_selectable]).alias('pj')
- pj, sj, sec, adapter = joincond.join_targets(
+ pj, sj, sec, adapter, ds = joincond.join_targets(
left,
joincond.child_selectable,
True)
)
right = select([joincond.child_selectable]).alias('pj')
- pj, sj, sec, adapter = joincond.join_targets(
+ pj, sj, sec, adapter, ds = joincond.join_targets(
joincond.parent_selectable,
right,
True)
def test_join_targets_o2m_plain(self):
joincond = self._join_fixture_o2m()
- pj, sj, sec, adapter = joincond.join_targets(
+ pj, sj, sec, adapter, ds = joincond.join_targets(
joincond.parent_selectable,
joincond.child_selectable,
False)
def test_join_targets_o2m_left_aliased(self):
joincond = self._join_fixture_o2m()
left = select([joincond.parent_selectable]).alias('pj')
- pj, sj, sec, adapter = joincond.join_targets(
+ pj, sj, sec, adapter, ds = joincond.join_targets(
left,
joincond.child_selectable,
True)
def test_join_targets_o2m_right_aliased(self):
joincond = self._join_fixture_o2m()
right = select([joincond.child_selectable]).alias('pj')
- pj, sj, sec, adapter = joincond.join_targets(
+ pj, sj, sec, adapter, ds = joincond.join_targets(
joincond.parent_selectable,
right,
True)