From: Mike Bayer Date: Thu, 15 Oct 2009 20:52:06 +0000 (+0000) Subject: - A column can be added to a joined-table declarative X-Git-Tag: rel_0_6beta1~251 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6535456ea6eabb5b0fa5a76abfb14cc7f663bd75;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - A column can be added to a joined-table declarative superclass after the class has been constructed (i.e. via class-level attribute assignment), and the column will be propagated down to subclasses. [ticket:1570] This is the reverse situation as that of [ticket:1523], fixed in 0.5.6. --- diff --git a/CHANGES b/CHANGES index 9fa1632a23..f2549eca29 100644 --- a/CHANGES +++ b/CHANGES @@ -482,6 +482,13 @@ CHANGES Trusted_Connection when constructing pyodbc connect arguments [ticket:1561] +- ext + - A column can be added to a joined-table declarative + superclass after the class has been constructed + (i.e. via class-level attribute assignment), and + the column will be propagated down to + subclasses. [ticket:1570] This is the reverse + situation as that of [ticket:1523], fixed in 0.5.6. 0.5.6 ===== diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index a9dad91766..b15abc114f 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -559,6 +559,7 @@ class Mapper(object): if mc is not None: # if the column is in the local table but not the mapped table, # this corresponds to adding a column after the fact to the local table. + # [ticket:1523] self.mapped_table._reset_exported() mc = self.mapped_table.corresponding_column(c) if mc is None: @@ -577,7 +578,24 @@ class Mapper(object): if isinstance(prop, ColumnProperty): col = self.mapped_table.corresponding_column(prop.columns[0]) - # col might not be present! the selectable given to the mapper need not include "deferred" + + # if the column is not present in the mapped table, + # test if a column has been added after the fact to the parent table + # (or their parent, etc.) + # [ticket:1570] + if col is None and self.inherits: + path = [self] + for m in self.inherits.iterate_to_root(): + col = m.local_table.corresponding_column(prop.columns[0]) + if col is not None: + for m2 in path: + m2.mapped_table._reset_exported() + col = self.mapped_table.corresponding_column(prop.columns[0]) + break + path.append(m) + + # otherwise, col might not be present! the selectable given + # to the mapper need not include "deferred" # columns (included in zblog tests) if col is None: col = prop.columns[0] diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index 6bd45833c7..c54e34e67a 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -1007,6 +1007,66 @@ class DeclarativeInheritanceTest(DeclarativeTestBase): eq_(sess.query(Person).first(), Engineer(primary_language='java', name='dilbert') ) + + def test_add_parentcol_after_the_fact(self): + class Person(Base, ComparableEntity): + __tablename__ = 'people' + id = Column('id', Integer, primary_key=True, test_needs_autoincrement=True) + discriminator = Column('type', String(50)) + __mapper_args__ = {'polymorphic_on':discriminator} + + class Engineer(Person): + __tablename__ = 'engineers' + __mapper_args__ = {'polymorphic_identity':'engineer'} + primary_language = Column(String(50)) + id = Column('id', Integer, ForeignKey('people.id'), primary_key=True) + + Person.name = Column('name', String(50)) + + Base.metadata.create_all() + + sess = create_session() + e1 = Engineer(primary_language='java', name='dilbert') + sess.add(e1) + sess.flush() + sess.expunge_all() + + eq_(sess.query(Person).first(), + Engineer(primary_language='java', name='dilbert') + ) + + def test_add_sub_parentcol_after_the_fact(self): + class Person(Base, ComparableEntity): + __tablename__ = 'people' + id = Column('id', Integer, primary_key=True, test_needs_autoincrement=True) + discriminator = Column('type', String(50)) + __mapper_args__ = {'polymorphic_on':discriminator} + + class Engineer(Person): + __tablename__ = 'engineers' + __mapper_args__ = {'polymorphic_identity':'engineer'} + primary_language = Column(String(50)) + id = Column('id', Integer, ForeignKey('people.id'), primary_key=True) + + class Admin(Engineer): + __tablename__ = 'admins' + __mapper_args__ = {'polymorphic_identity':'admin'} + workstation = Column(String(50)) + id = Column('id', Integer, ForeignKey('engineers.id'), primary_key=True) + + Person.name = Column('name', String(50)) + + Base.metadata.create_all() + + sess = create_session() + e1 = Admin(primary_language='java', name='dilbert', workstation='foo') + sess.add(e1) + sess.flush() + sess.expunge_all() + + eq_(sess.query(Person).first(), + Admin(primary_language='java', name='dilbert', workstation='foo') + ) def test_subclass_mixin(self): class Person(Base, ComparableEntity):