]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- A column can be added to a joined-table subclass after
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 1 Sep 2009 22:26:23 +0000 (22:26 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 1 Sep 2009 22:26:23 +0000 (22:26 +0000)
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]
- added an additional test in test_mappers for "added nonexistent column",
even though this test is already in test_query its more appropriate within
"mapper configuration" tests.

CHANGES
lib/sqlalchemy/orm/mapper.py
test/ext/test_declarative.py
test/orm/test_mapper.py

diff --git a/CHANGES b/CHANGES
index 6d680cfa8045fc6a5e5af0682c96f6ce412fb86f..f8786a1d0aa42d26c6b9494d7e8d75c3e2d1a34f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -479,6 +479,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.
index 28565bd82fc404fe02d0ce85ec5b5b0daf190565..907fcd5c3ad6b84d943647cc6d6ba30ffb18b02d 100644 (file)
@@ -549,9 +549,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:
index 63beb54e7a1352616935f4da1243e31fc8ad7a0b..b64ad23e1c6f4a14703d9fb40237ed0c8055ddd4 100644 (file)
@@ -956,7 +956,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'
index 250992049f456ee3ce53d6fe07777a1b20399b4d..146baebd24f619c365dd019b7b17a41cca5fbcb5 100644 (file)
@@ -158,6 +158,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"""