- ORMAdapter filters all replacements against a non-compatible 'parentmapper' annotation
- Other filterings, like
query(A).join(A.bs).filter(B.foo=='bar'), were erroneously
adapting "B.foo" as though it were an "A".
- Fixed bugs in Query regarding simultaneous selection of
multiple joined-table inheritance entities with common base
- classes, previously the adaption applied to "e2" on
- "e1 JOIN e2" would be partially applied to "e1". Additionally,
- comparisons on relations (i.e. Entity2.related==e2)
- were not getting adapted correctly.
+ classes:
+
+ - previously the adaption applied to "B" on
+ "A JOIN B" would be erroneously partially applied
+ to "A".
+
+ - comparisons on relations (i.e. A.related==someb)
+ were not getting adapted when they should.
+
+ - Other filterings, like
+ query(A).join(A.bs).filter(B.foo=='bar'), were erroneously
+ adapting "B.foo" as though it were an "A".
- sql
- Fixed missing _label attribute on Function object, others
return lambda obj: None
def visit_column(self, clause):
- if 'parententity' in clause._annotations:
- key = clause._annotations['parententity']._get_col_to_prop(clause).key
+ if 'parentmapper' in clause._annotations:
+ key = clause._annotations['parentmapper']._get_col_to_prop(clause).key
else:
key = clause.key
get_corresponding_attr = operator.attrgetter(key)
if self.adapter:
return self.adapter(self.prop.columns[0])
else:
- return self.prop.columns[0]._annotate({"parententity": self.mapper})
+ return self.prop.columns[0]._annotate({"parententity": self.mapper, "parentmapper":self.mapper})
def operate(self, op, *other, **kwargs):
return op(self.__clause_element__(), *other, **kwargs)
"""
def __init__(self, entity, equivalents=None, chain_to=None):
- mapper, selectable, is_aliased_class = _entity_info(entity)
+ self.mapper, selectable, is_aliased_class = _entity_info(entity)
if is_aliased_class:
self.aliased_class = entity
else:
self.aliased_class = None
sql_util.ColumnAdapter.__init__(self, selectable, equivalents, chain_to)
+ def replace(self, elem):
+ entity = elem._annotations.get('parentmapper', None)
+ if not entity or entity.isa(self.mapper):
+ return sql_util.ColumnAdapter.replace(self, elem)
+ else:
+ return None
+
class AliasedClass(object):
"""Represents an 'alias'ed form of a mapped class for usage with Query.
self.__name__ = 'AliasedClass_' + str(self.__target)
def __adapt_element(self, elem):
- return self.__adapter.traverse(elem)._annotate({'parententity': self})
+ return self.__adapter.traverse(elem)._annotate({'parententity': self, 'parentmapper':self.__mapper})
def __adapt_prop(self, prop):
existing = getattr(self.__target, prop.key)
sess.query(Engineer).join('reports_to', aliased=True).filter(Person.name=='dogbert').first(),
Engineer(name='dilbert'))
-class SelfReferentialTestJoinedToJoined(ORMTest):
+class SelfReferentialJ2JTest(ORMTest):
keep_mappers = True
def define_tables(self, metadata):
sess.query(Engineer).join('reports_to', aliased=True).filter(Manager.name=='dogbert').first(),
Engineer(name='dilbert'))
+ def test_filter_aliasing(self):
+ m1 = Manager(name='dogbert')
+ m2 = Manager(name='foo')
+ e1 = Engineer(name='wally', primary_language='java', reports_to=m1)
+ e2 = Engineer(name='dilbert', primary_language='c++', reports_to=m2)
+ e3 = Engineer(name='etc', primary_language='c++')
+ sess = create_session()
+ sess.add_all([m1, m2, e1, e2, e3])
+ sess.flush()
+ sess.expunge_all()
+
+ # filter aliasing applied to Engineer doesn't whack Manager
+ self.assertEquals(
+ sess.query(Manager).join(Manager.engineers).filter(Manager.name=='dogbert').all(),
+ [m1]
+ )
+
+ self.assertEquals(
+ sess.query(Manager).join(Manager.engineers).filter(Engineer.name=='dilbert').all(),
+ [m2]
+ )
+
+ self.assertEquals(
+ sess.query(Manager, Engineer).join(Manager.engineers).order_by(Manager.name.desc()).all(),
+ [
+ (m2, e2),
+ (m1, e1),
+ ]
+ )
+
def test_relation_compare(self):
m1 = Manager(name='dogbert')
m2 = Manager(name='foo')
sess.query(Manager).join(Manager.engineers).filter(Engineer.reports_to==m1).all(),
[m1]
)
-
-
+
class M2MFilterTest(ORMTest):