]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug where "middle" class in a polymorphic hierarchy
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 2 Feb 2011 23:11:17 +0000 (18:11 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 2 Feb 2011 23:11:17 +0000 (18:11 -0500)
would have no 'polymorphic_on' column if it didn't also
specify a 'polymorphic_identity', leading to strange
errors upon refresh, wrong class loaded when querying
from that target. [ticket:2038]

CHANGES
lib/sqlalchemy/orm/mapper.py
test/ext/test_declarative.py
test/orm/inheritance/test_basic.py

diff --git a/CHANGES b/CHANGES
index 849ec820459031b356e368efb8a4b6f75142e2bd..10f2699322c2164f211aac2905e816f8d88ae1e4 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -14,6 +14,12 @@ CHANGES
     has no primary keys on the locally mapped table
     (but has pks on the superclass table).  [ticket:2019]
 
+  - Fixed bug where "middle" class in a polymorphic hierarchy
+    would have no 'polymorphic_on' column if it didn't also
+    specify a 'polymorphic_identity', leading to strange
+    errors upon refresh, wrong class loaded when querying
+    from that target. [ticket:2038]
+
 - sql
   - Column.copy(), as used in table.tometadata(), copies the 
     'doc' attribute.  [ticket:2028]
index 0245b7601b03e270ae044cc17d3564c1c4e2b6d6..230e22d32b5b66f990b3707946ed1bca9a720f27 100644 (file)
@@ -303,16 +303,17 @@ class Mapper(object):
 
             if self.polymorphic_identity is not None:
                 self.polymorphic_map[self.polymorphic_identity] = self
-                if self.polymorphic_on is None:
-                    for mapper in self.iterate_to_root():
-                        # try to set up polymorphic on using
-                        # correesponding_column(); else leave
-                        # as None
-                        if mapper.polymorphic_on is not None:
-                            self.polymorphic_on = \
-                                    self.mapped_table.corresponding_column(
-                                                        mapper.polymorphic_on)
-                            break
+
+            if self.polymorphic_on is None:
+                for mapper in self.iterate_to_root():
+                    # try to set up polymorphic on using
+                    # correesponding_column(); else leave
+                    # as None
+                    if mapper.polymorphic_on is not None:
+                        self.polymorphic_on = \
+                                self.mapped_table.corresponding_column(
+                                                    mapper.polymorphic_on)
+                        break
         else:
             self._all_tables = set()
             self.base_mapper = self
index ed6a64cf75b99bf7ed9dc484c090b9810994cd8d..37d92812925973c15ad099aca61bbe94ec376377 100644 (file)
@@ -1146,7 +1146,8 @@ class DeclarativeInheritanceTest(DeclarativeTestBase):
             primary_language = Column(String(50))
 
         assert 'inherits' not in Person.__mapper_args__
-        assert class_mapper(Engineer).polymorphic_on is None
+        assert class_mapper(Engineer).polymorphic_identity is None
+        assert class_mapper(Engineer).polymorphic_on is Person.__table__.c.type
 
     def test_custom_join_condition(self):
 
index 164d4d4075a3cf61eefada12254306b2ffde944f..805644844d96b22001be6ef02b72e11ef58043ba 100644 (file)
@@ -1442,6 +1442,50 @@ class PKDiscriminatorTest(_base.MappedTest):
         assert a.name=='a1new'
         assert p.name=='p1new'
 
+class NoPolyIdentInMiddleTest(_base.MappedTest):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table('base', metadata,
+            Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
+            Column('type', String(50), nullable=False),
+        )
+
+    @classmethod
+    def setup_classes(cls):
+        class A(_base.BasicEntity):
+            pass
+        class B(A):
+            pass
+        class C(B):
+            pass
+
+    @classmethod
+    @testing.resolve_artifact_names
+    def setup_mappers(cls):
+        mapper(A, base, polymorphic_on=base.c.type)
+        mapper(B, inherits=A)
+        mapper(C, inherits=B, polymorphic_identity='c')
+
+    @testing.resolve_artifact_names
+    def test_load_from_middle(self):
+        s = Session()
+        s.add(C())
+        o = s.query(B).first()
+        eq_(o.type, 'c')
+        assert isinstance(o, C)
+
+    @testing.resolve_artifact_names
+    def test_load_from_base(self):
+        s = Session()
+        s.add(C())
+        o = s.query(A).first()
+        eq_(o.type, 'c')
+        assert isinstance(o, C)
+
+    @testing.resolve_artifact_names
+    def test_discriminator(self):
+        assert class_mapper(B).polymorphic_on is base.c.type
+        assert class_mapper(C).polymorphic_on is base.c.type
 
 class DeleteOrphanTest(_base.MappedTest):
     @classmethod