From: Mike Bayer Date: Mon, 16 Dec 2013 20:25:48 +0000 (-0500) Subject: - Fixed bug when using joined table inheritance from a table to a X-Git-Tag: rel_0_8_5~75 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e294767148c7293170f36d56605853eb76f20714;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug when using joined table inheritance from a table to a select/alias on the base, where the PK columns were also not same named; the persistence system would fail to copy primary key values from the base table to the inherited table upon INSERT. [ticket:2885] --- diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 6ca5a6e698..3b30333f28 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -11,6 +11,16 @@ .. changelog:: :version: 0.8.5 + .. change:: + :tags: bug, orm + :versions: 0.9.0b2 + :tickets: 2885 + + Fixed bug when using joined table inheritance from a table to a + select/alias on the base, where the PK columns were also not same + named; the persistence system would fail to copy primary key values + from the base table to the inherited table upon INSERT. + .. change:: :tags: bug, orm :versions: 0.9.0b2 diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 177e31651a..e908054fdb 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2116,7 +2116,9 @@ class Mapper(_InspectionAttr): for m in self.iterate_to_root(): if m._inherits_equated_pairs and \ cols.intersection( - [l for l, r in m._inherits_equated_pairs]): + util.reduce(set.union, + [l.proxy_set for l, r in m._inherits_equated_pairs]) + ): result[table].append((m, m._inherits_equated_pairs)) return result diff --git a/test/orm/inheritance/test_selects.py b/test/orm/inheritance/test_selects.py index dd9c8c8b81..94f5faf8fa 100644 --- a/test/orm/inheritance/test_selects.py +++ b/test/orm/inheritance/test_selects.py @@ -1,50 +1,101 @@ -from sqlalchemy import * -from sqlalchemy.orm import * +from sqlalchemy import String, Integer, ForeignKey, select +from sqlalchemy.orm import mapper, Session from sqlalchemy import testing -from sqlalchemy.testing import fixtures +from sqlalchemy.testing import fixtures, eq_ +from sqlalchemy.testing.schema import Table, Column class InheritingSelectablesTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): - global foo, bar, baz foo = Table('foo', metadata, Column('a', String(30), primary_key=1), Column('b', String(30), nullable=0)) - bar = foo.select(foo.c.b == 'bar').alias('bar') - baz = foo.select(foo.c.b == 'baz').alias('baz') + cls.tables.bar = foo.select(foo.c.b == 'bar').alias('bar') + cls.tables.baz = foo.select(foo.c.b == 'baz').alias('baz') def test_load(self): + foo, bar, baz = self.tables.foo, self.tables.bar, self.tables.baz # TODO: add persistence test also testing.db.execute(foo.insert(), a='not bar', b='baz') testing.db.execute(foo.insert(), a='also not bar', b='baz') testing.db.execute(foo.insert(), a='i am bar', b='bar') testing.db.execute(foo.insert(), a='also bar', b='bar') - class Foo(fixtures.ComparableEntity): pass - class Bar(Foo): pass - class Baz(Foo): pass + class Foo(fixtures.ComparableEntity): + pass + class Bar(Foo): + pass + class Baz(Foo): + pass mapper(Foo, foo, polymorphic_on=foo.c.b) mapper(Baz, baz, - with_polymorphic=('*', foo.join(baz, foo.c.b=='baz').alias('baz')), + with_polymorphic=('*', foo.join(baz, foo.c.b == 'baz').alias('baz')), inherits=Foo, - inherit_condition=(foo.c.a==baz.c.a), + inherit_condition=(foo.c.a == baz.c.a), inherit_foreign_keys=[baz.c.a], polymorphic_identity='baz') mapper(Bar, bar, - with_polymorphic=('*', foo.join(bar, foo.c.b=='bar').alias('bar')), + with_polymorphic=('*', foo.join(bar, foo.c.b == 'bar').alias('bar')), inherits=Foo, - inherit_condition=(foo.c.a==bar.c.a), + inherit_condition=(foo.c.a == bar.c.a), inherit_foreign_keys=[bar.c.a], polymorphic_identity='bar') - s = sessionmaker(bind=testing.db)() + s = Session() assert [Baz(), Baz(), Bar(), Bar()] == s.query(Foo).order_by(Foo.b.desc()).all() assert [Bar(), Bar()] == s.query(Bar).all() + +class JoinFromSelectPersistenceTest(fixtures.MappedTest): + """test for [ticket:2885]""" + + @classmethod + def define_tables(cls, metadata): + Table('base', metadata, + Column('id', Integer, primary_key=True, + test_needs_autoincrement=True), + Column('type', String(50)) + ) + Table('child', metadata, + # 1. name of column must be different, so that we rely on + # mapper._table_to_equated to link the two cols + Column('child_id', Integer, ForeignKey('base.id'), primary_key=True), + Column('name', String(50)) + ) + + @classmethod + def setup_classes(cls): + class Base(cls.Comparable): + pass + class Child(Base): + pass + + def test_map_to_select(self): + Base, Child = self.classes.Base, self.classes.Child + base, child = self.tables.base, self.tables.child + + base_select = select([base]).alias() + mapper(Base, base_select, polymorphic_on=base_select.c.type, + polymorphic_identity='base') + mapper(Child, child, inherits=Base, + polymorphic_identity='child') + + sess = Session() + + # 2. use an id other than "1" here so can't rely on + # the two inserts having the same id + c1 = Child(id=12, name='c1') + sess.add(c1) + + sess.commit() + sess.close() + + c1 = sess.query(Child).one() + eq_(c1.name, 'c1')