]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug where the :class:`.AutomapBase` class of the
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 9 Feb 2014 02:24:13 +0000 (21:24 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 9 Feb 2014 02:24:13 +0000 (21:24 -0500)
new automap extension would fail if classes
were pre-arranged in single or potentially joined inheritance patterns.
The repaired joined inheritance issue could also potentially apply when
using :class:`.DeferredReflection` as well.

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/ext/automap.py
lib/sqlalchemy/ext/declarative/base.py
test/ext/test_automap.py

index 87f1b2494970f3734f07e264dff2f0b68d2bb38e..1db54d85cc71d87d72aed0eba7b47488016ac1e3 100644 (file)
 .. changelog::
     :version: 0.9.3
 
+    .. change::
+        :tags: bug, ext
+
+        Fixed bug where the :class:`.AutomapBase` class of the
+        new automap extension would fail if classes
+        were pre-arranged in single or potentially joined inheritance patterns.
+        The repaired joined inheritance issue could also potentially apply when
+        using :class:`.DeferredReflection` as well.
+
+
     .. change::
         :tags: bug, sql
         :pullreq: github:67
index 7a1512f6a05a9057a2fd2dac08428920bc187c53..b98ac17073f5d902d7b3da10fb851815cfff2b6d 100644 (file)
@@ -598,7 +598,8 @@ class AutomapBase(object):
 
         table_to_map_config = dict(
                                 (m.local_table, m)
-                                for m in _DeferredMapperConfig.classes_for_base(cls)
+                                for m in _DeferredMapperConfig.
+                                    classes_for_base(cls, sort=False)
                             )
 
         many_to_many = []
@@ -635,7 +636,8 @@ class AutomapBase(object):
                             name_for_scalar_relationship,
                             name_for_collection_relationship,
                             generate_relationship)
-        for map_config in table_to_map_config.values():
+
+        for map_config in _DeferredMapperConfig.classes_for_base(cls):
             map_config.map()
 
 
@@ -718,6 +720,8 @@ def _relationships_for_fks(automap_base, map_config, table_to_map_config,
     local_table = map_config.local_table
     local_cls = map_config.cls
 
+    if local_table is None:
+        return
     for constraint in local_table.constraints:
         if isinstance(constraint, ForeignKeyConstraint):
             fks = constraint.elements
index a764f126b42d493571d874216e0cf102afcbbb9f..4fda9c734ee8527172b15754bf10aea7c865af50 100644 (file)
@@ -12,6 +12,7 @@ from ...orm.properties import ColumnProperty, CompositeProperty
 from ...orm.attributes import QueryableAttribute
 from ...orm.base import _is_mapped_class
 from ... import util, exc
+from ...util import topological
 from ...sql import expression
 from ... import event
 from . import clsregistry
@@ -432,9 +433,30 @@ class _DeferredMapperConfig(_MapperConfig):
 
 
     @classmethod
-    def classes_for_base(cls, base_cls):
-        return [m for m in cls._configs.values()
-                    if issubclass(m.cls, base_cls)]
+    def classes_for_base(cls, base_cls, sort=True):
+        classes_for_base = [m for m in cls._configs.values()
+                        if issubclass(m.cls, base_cls)]
+        if not sort:
+            return classes_for_base
+
+        all_m_by_cls = dict(
+                            (m.cls, m)
+                            for m in classes_for_base
+                        )
+
+        tuples = []
+        for m_cls in all_m_by_cls:
+            tuples.extend(
+                    (all_m_by_cls[base_cls], all_m_by_cls[m_cls])
+                    for base_cls in m_cls.__bases__
+                    if base_cls in all_m_by_cls
+                )
+        return list(
+            topological.sort(
+                tuples,
+                classes_for_base
+            )
+        )
 
     def map(self):
         self._configs.pop(self._cls, None)
index 9db85879dbd1664e60b2c9e561b13f14207fb8e2..3a2d4d31c707406574f74b1b1d0cbe92cf1f7410 100644 (file)
@@ -4,6 +4,8 @@ from sqlalchemy.ext.automap import automap_base
 from sqlalchemy.orm import relationship, interfaces, backref
 from sqlalchemy.ext.automap import generate_relationship
 from sqlalchemy.testing.mock import Mock, call
+from sqlalchemy import Column, String, Table, Integer, ForeignKey
+from sqlalchemy import testing
 
 class AutomapTest(fixtures.MappedTest):
     @classmethod
@@ -144,3 +146,66 @@ class AutomapTest(fixtures.MappedTest):
                 (Base, interfaces.MANYTOONE, "users"),
                 (Base, interfaces.ONETOMANY, "addresses_collection"),
         ])
+
+
+class AutomapInhTest(fixtures.MappedTest):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table('single', metadata,
+                Column('id', Integer, primary_key=True),
+                Column('type', String(10))
+            )
+
+        Table('joined_base', metadata,
+                Column('id', Integer, primary_key=True),
+                Column('type', String(10))
+            )
+
+        Table('joined_inh', metadata,
+                Column('id', Integer, ForeignKey('joined_base.id'), primary_key=True),
+            )
+
+        FixtureTest.define_tables(metadata)
+
+    def test_single_inheritance_reflect(self):
+        Base = automap_base()
+
+        class Single(Base):
+            __tablename__ = 'single'
+
+            type = Column(String)
+
+            __mapper_args__ = {"polymorphic_identity": "u0",
+                "polymorphic_on": type}
+
+        class SubUser1(Single):
+            __mapper_args__ = {"polymorphic_identity": "u1"}
+
+        class SubUser2(Single):
+            __mapper_args__ = {"polymorphic_identity": "u2"}
+
+        Base.prepare(engine=testing.db, reflect=True)
+
+        assert SubUser2.__mapper__.inherits is Single.__mapper__
+
+    def test_joined_inheritance_reflect(self):
+        Base = automap_base()
+
+        class Joined(Base):
+            __tablename__ = 'joined_base'
+
+            type = Column(String)
+
+            __mapper_args__ = {"polymorphic_identity": "u0",
+                "polymorphic_on": type}
+
+        class SubJoined(Joined):
+            __tablename__ = 'joined_inh'
+            __mapper_args__ = {"polymorphic_identity": "u1"}
+
+
+        Base.prepare(engine=testing.db, reflect=True)
+
+        assert SubJoined.__mapper__.inherits is Joined.__mapper__
+
+