- further rework of the recent polymorphic relationship refactorings, as well
as the mechanics of relationships overall. Allows more accurate ORM behavior
with relationships from/to/between polymorphic mappers, as well as their usage
- with Query, SelectResults. tickets include [ticket:439], [ticket:441].
+ with Query, SelectResults. tickets include [ticket:439], [ticket:441], [ticket:448].
relationship mechanics are still a work in progress, more to come !
- eager relation to an inheriting mapper wont fail if no rows returned for
the relationship.
self.collection_class = collection_class
self.passive_deletes = passive_deletes
self.remote_side = util.to_set(remote_side)
+ self._parent_join_cache = {}
if cascade is not None:
self.cascade = mapperutil.CascadeOptions(cascade)
# as we will be using the polymorphic selectables (i.e. select_table argument to Mapper) to figure this out,
# first create maps of all the "equivalent" columns, since polymorphic selectables will often munge
# several "equivalent" columns (such as parent/child fk cols) into just one column.
- parent_equivalents = self.parent._get_inherited_column_equivalents()
target_equivalents = self.mapper._get_inherited_column_equivalents()
# if the target mapper loads polymorphically, adapt the clauses to the target's selectable
self.polymorphic_primaryjoin = self.primaryjoin.copy_container()
self.polymorphic_secondaryjoin = self.secondaryjoin and self.secondaryjoin.copy_container() or None
- # if the parent mapper loads polymorphically, adapt the clauses to the parent's selectable
- if self.parent.select_table is not self.parent.mapped_table:
- if self.direction is sync.ONETOMANY:
- self.polymorphic_primaryjoin.accept_visitor(sql_util.ClauseAdapter(self.parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
- elif self.direction is sync.MANYTOONE:
- self.polymorphic_primaryjoin.accept_visitor(sql_util.ClauseAdapter(self.parent.select_table, include=self.foreignkey, equivalents=parent_equivalents))
- elif self.secondaryjoin:
- self.polymorphic_primaryjoin.accept_visitor(sql_util.ClauseAdapter(self.parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
-
+
#print "KEY", self.key, "PARENT", str(self.parent)
#print "KEY", self.key, "REG PRIMARY JOIN", str(self.primaryjoin)
#print "KEY", self.key, "POLY PRIMARY JOIN", str(self.polymorphic_primaryjoin)
raise exceptions.ArgumentError("Cant determine relation direction for '%s' on mapper '%s' with primary join '%s' - no foreign key relationship is expressed within the join condition. Specify 'foreignkey' argument." %(self.key, str(self.parent), str(self.primaryjoin)))
self.foreignkey = foreignkeys
- def get_join(self):
- if self.polymorphic_secondaryjoin is not None:
- return self.polymorphic_primaryjoin & self.polymorphic_secondaryjoin
- else:
- return self.polymorphic_primaryjoin
+ def get_join(self, parent):
+ try:
+ return self._parent_join_cache[parent]
+ except KeyError:
+ parent_equivalents = parent._get_inherited_column_equivalents()
+ primaryjoin = self.polymorphic_primaryjoin.copy_container()
+ if self.secondaryjoin is not None:
+ secondaryjoin = self.polymorphic_secondaryjoin.copy_container()
+ else:
+ secondaryjoin = None
+ if self.direction is sync.ONETOMANY:
+ primaryjoin.accept_visitor(sql_util.ClauseAdapter(parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
+ elif self.direction is sync.MANYTOONE:
+ primaryjoin.accept_visitor(sql_util.ClauseAdapter(parent.select_table, include=self.foreignkey, equivalents=parent_equivalents))
+ elif self.secondaryjoin:
+ primaryjoin.accept_visitor(sql_util.ClauseAdapter(parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
+ if secondaryjoin is not None:
+ j = primaryjoin & secondaryjoin
+ else:
+ j = primaryjoin
+ self._parent_join_cache[parent] = j
+ return j
+
def register_dependencies(self, uowcommit):
if not self.viewonly:
self._dependency_processor.register_dependencies(uowcommit)
session.save(car2)
session.flush()
-# for activeCars in SelectResults(session.query(Car)).join_to('status').select(status.c.name=="active"):
-# print activeCars
- for activePerson in SelectResults(session.query(Person)).join_to('status').select(status.c.name=="active"):
- print activePerson
-# for activePerson in SelectResults(session.query(Person)).join_to('status').select_by(name="active"):
-# print activePerson
-
-
+ # test these twice because theres caching involved
+ for x in range(0, 2):
+ r = SelectResults(session.query(Person)).select_by(people.c.name.like('%2')).join_to('status').select_by(name="active")
+ assert str(list(r)) == "[Manager M2, category YYYYYYYYY, status Status active, Engineer E2, field X, status Status active]"
+ r = SelectResults(session.query(Engineer)).join_to('status').select(people.c.name.in_('E2', 'E3', 'E4', 'M4', 'M2', 'M1') & (status.c.name=="active"))
+ assert str(list(r)) == "[Engineer E2, field X, status Status active, Engineer E3, field X, status Status active]"
+
class MultiLevelTest(testbase.ORMTest):
def define_tables(self, metadata):
global table_Employee, table_Engineer, table_Manager