From: Mike Bayer Date: Tue, 1 Sep 2009 22:27:28 +0000 (+0000) Subject: - merged r6312 of trunk for [ticket:1523] X-Git-Tag: rel_0_5_6~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f91c12a2edb8694d56b6c8a6a9b3eeeb12a5556;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - merged r6312 of trunk for [ticket:1523] --- diff --git a/CHANGES b/CHANGES index 6f579532df..bfae7b8093 100644 --- a/CHANGES +++ b/CHANGES @@ -114,6 +114,14 @@ CHANGES __table_args__ is passed as a tuple with no dict argument. Improved documentation. [ticket:1468] + - A column can be added to a joined-table subclass after + the class has been constructed (i.e. via class-level + attribute assignment). The column is added to the underlying + Table as always, but now the mapper will rebuild its + "join" to include the new column, instead of raising + an error about "no such column, use column_property() + instead". [ticket:1523] + - test - Added examples into the test suite so they get exercised regularly and cleaned up a couple deprecation warnings. diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 078056a01c..b8842a5f86 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -547,9 +547,16 @@ class Mapper(object): for c in columns: mc = self.mapped_table.corresponding_column(c) if not mc: - raise sa_exc.ArgumentError("Column '%s' is not represented in mapper's table. " - "Use the `column_property()` function to force this column " - "to be mapped as a read-only attribute." % c) + mc = self.local_table.corresponding_column(c) + if mc: + # 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. + self.mapped_table._reset_exported() + mc = self.mapped_table.corresponding_column(c) + if not mc: + raise sa_exc.ArgumentError("Column '%s' is not represented in mapper's table. " + "Use the `column_property()` function to force this column " + "to be mapped as a read-only attribute." % c) mapped_column.append(mc) prop = ColumnProperty(*mapped_column) else: diff --git a/test/ext/test_declarative.py b/test/ext/test_declarative.py index 224f41731a..f487778869 100644 --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@ -862,7 +862,34 @@ class DeclarativeInheritanceTest(DeclarativeTestBase): def go(): assert sess.query(Person).filter(Manager.name=='dogbert').one().id self.assert_sql_count(testing.db, go, 1) + + def test_add_subcol_after_the_fact(self): + class Person(Base, ComparableEntity): + __tablename__ = 'people' + id = Column('id', Integer, primary_key=True, test_needs_autoincrement=True) + name = Column('name', String(50)) + discriminator = Column('type', String(50)) + __mapper_args__ = {'polymorphic_on':discriminator} + + class Engineer(Person): + __tablename__ = 'engineers' + __mapper_args__ = {'polymorphic_identity':'engineer'} + id = Column('id', Integer, ForeignKey('people.id'), primary_key=True) + + Engineer.primary_language = Column('primary_language', 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_subclass_mixin(self): class Person(Base, ComparableEntity): __tablename__ = 'people' diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index af8ea2aa4d..774ec47c02 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -159,6 +159,12 @@ class MapperTest(_fixtures.FixtureTest): assert 'email_addres' not in a.__dict__ eq_(a.email_address, 'jack@bean.com') + @testing.resolve_artifact_names + def test_column_not_present(self): + assert_raises_message(sa.exc.ArgumentError, "not represented in mapper's table", mapper, User, users, properties={ + 'foo':addresses.c.user_id + }) + @testing.resolve_artifact_names def test_bad_constructor(self): """If the construction of a mapped class fails, the instance does not get placed in the session"""