From: Mike Bayer Date: Wed, 16 Sep 2009 19:50:57 +0000 (+0000) Subject: merged r6355 from trunk for #1543 X-Git-Tag: rel_0_5_7~38 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72d9a6ff607dc98c3a0c0c8f9ce3b4e66d4b1180;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git merged r6355 from trunk for #1543 --- diff --git a/CHANGES b/CHANGES index 79d08eb90b..e5bb8cd7b9 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,19 @@ ======= CHANGES ======= + +0.5.7 +===== +- orm + - contains_eager() now works with the automatically + generated subquery that results when you say + "query(Parent).join(Parent.somejoinedsubclass)", i.e. + when Parent joins to a joined-table-inheritance subclass. + Previously contains_eager() would erroneously add the + subclass table to the query separately producing a + cartesian product. An example is in the ticket + description. [ticket:1543] + 0.5.6 ===== - orm diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index fd767c8655..bdf99980cd 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -836,7 +836,10 @@ class LoadEagerFromAliasOption(PropertyOption): self.alias = prop.target.alias(self.alias) query._attributes[("user_defined_eager_row_processor", paths[-1])] = sql_util.ColumnAdapter(self.alias) else: - query._attributes[("user_defined_eager_row_processor", paths[-1])] = None + (mapper, propname) = paths[-1][-2:] + prop = mapper.get_property(propname, resolve_synonyms=True) + adapter = query._polymorphic_adapters.get(prop.mapper, None) + query._attributes[("user_defined_eager_row_processor", paths[-1])] = adapter class _SingleParentValidator(interfaces.AttributeExtension): def __init__(self, prop): diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index f93dfd5476..acb9f369d5 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -398,6 +398,88 @@ class EagerTargetingTest(_base.MappedTest): eq_(node, B(id=1, name='b1',b_data='i')) eq_(node.children[0], B(id=2, name='b2',b_data='l')) +class EagerToSubclassTest(_base.MappedTest): + """Test eagerloads to subclass mappers""" + + run_setup_classes = 'once' + run_setup_mappers = 'once' + run_inserts = 'once' + run_deletes = None + + @classmethod + def define_tables(cls, metadata): + Table('parent', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(10)), + ) + + Table('base', metadata, + Column('id', Integer, primary_key=True), + Column('type', String(10)), + ) + + Table('sub', metadata, + Column('id', Integer, ForeignKey('base.id'), primary_key=True), + Column('data', String(10)), + Column('parent_id', Integer, ForeignKey('parent.id'), nullable=False) + ) + + @classmethod + @testing.resolve_artifact_names + def setup_classes(cls): + class Parent(_base.ComparableEntity): + pass + + class Base(_base.ComparableEntity): + pass + + class Sub(Base): + pass + + @classmethod + @testing.resolve_artifact_names + def setup_mappers(cls): + mapper(Parent, parent, properties={ + 'children':relation(Sub) + }) + mapper(Base, base, polymorphic_on=base.c.type, polymorphic_identity='b') + mapper(Sub, sub, inherits=Base, polymorphic_identity='s') + + @classmethod + @testing.resolve_artifact_names + def insert_data(cls): + sess = create_session() + p1 = Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]) + p2 = Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')]) + sess.add(p1) + sess.add(p2) + sess.flush() + + @testing.resolve_artifact_names + def test_eagerload(self): + sess = create_session() + def go(): + eq_( + sess.query(Parent).options(eagerload(Parent.children)).all(), + [ + Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]), + Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')]) + ] + ) + self.assert_sql_count(testing.db, go, 1) + + @testing.resolve_artifact_names + def test_contains_eager(self): + sess = create_session() + def go(): + eq_( + sess.query(Parent).join(Parent.children).options(contains_eager(Parent.children)).all(), + [ + Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]), + Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')]) + ] + ) + self.assert_sql_count(testing.db, go, 1) class FlushTest(_base.MappedTest): """test dependency sorting among inheriting mappers""" diff --git a/test/orm/inheritance/test_polymorph2.py b/test/orm/inheritance/test_polymorph2.py index 51b6d4970a..834f18fc86 100644 --- a/test/orm/inheritance/test_polymorph2.py +++ b/test/orm/inheritance/test_polymorph2.py @@ -436,8 +436,8 @@ class RelationTest5(_base.MappedTest): Column('car_id', Integer, primary_key=True), Column('owner', Integer, ForeignKey('people.person_id'))) - def testeagerempty(self): - """an easy one...test parent object with child relation to an inheriting mapper, using eager loads, + def test_eager_empty(self): + """test parent object with child relation to an inheriting mapper, using eager loads, works when there are no child objects present""" class Person(object): def __init__(self, **kwargs): diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py index 5b57e8f457..1dd96ef1b4 100644 --- a/test/orm/inheritance/test_query.py +++ b/test/orm/inheritance/test_query.py @@ -472,7 +472,7 @@ def _produce_test(select_type): [Engineer(name="dilbert", engineer_name="dilbert", primary_language="java", status="regular engineer", machines=[Machine(name="IBM ThinkPad"), Machine(name="IPhone")])] ) self.assert_sql_count(testing.db, go, 1) - + def test_join_to_subclass(self): sess = create_session() eq_(sess.query(Company).join(('employees', people.join(engineers))).filter(Engineer.primary_language=='java').all(), [c1])