diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2009-10-15 20:52:06 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2009-10-15 20:52:06 +0000 |
| commit | 6535456ea6eabb5b0fa5a76abfb14cc7f663bd75 (patch) | |
| tree | ad935eb1fc805f806213450198c2d368fb4486f2 | |
| parent | 3984cad7228fcc8fae9c3be93ecd86f6b08ed4b3 (diff) | |
| download | sqlalchemy-6535456ea6eabb5b0fa5a76abfb14cc7f663bd75.tar.gz | |
- 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.
| -rw-r--r-- | CHANGES | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 20 | ||||
| -rw-r--r-- | test/ext/test_declarative.py | 60 |
3 files changed, 86 insertions, 1 deletions
@@ -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 a9dad9176..b15abc114 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 6bd45833c..c54e34e67 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): |
