summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--lib/sqlalchemy/orm/mapper.py20
-rw-r--r--test/ext/test_declarative.py60
3 files changed, 86 insertions, 1 deletions
diff --git a/CHANGES b/CHANGES
index 9fa1632a2..f2549eca2 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 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):