]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Adapt from "localparent" in joinedloader
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 9 Jan 2017 19:16:22 +0000 (14:16 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 9 Jan 2017 19:21:20 +0000 (14:21 -0500)
Fixed bug involving joined eager loading against multiple entities
when polymorphic inheritance is also in use which would throw
"'NoneType' object has no attribute 'isa'".  The issue was introduced
by the fix for :ticket:`3611`.

Change-Id: I296ecda38c01ec8f69dcd843beaebed6949cecfa
Fixes: #3884
(cherry picked from commit 51a72503b0279ca71ee6f0454bfd36a4c84d508f)

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/orm/strategies.py
test/orm/inheritance/test_relationship.py

index 87441414a91c5d614a26d8f8a0e26ace0cdf3562..f87133d31beaac253e2fbb1da38a305a81f6f74c 100644 (file)
     .. include:: changelog_07.rst
         :start-line: 5
 
+.. changelog::
+    :version: 1.0.17
+
+    .. change::
+        :tags: bug, orm
+        :tickets: 3884
+        :versions: 1.1.5
+
+        Fixed bug involving joined eager loading against multiple entities
+        when polymorphic inheritance is also in use which would throw
+        "'NoneType' object has no attribute 'isa'".  The issue was introduced
+        by the fix for :ticket:`3611`.
+
 .. changelog::
     :version: 1.0.16
     :released: November 15, 2016
index 48b2b0298fae718de321914f3fd15e7d71bb8003..88b51d88ea55df95041e4c77094d0807c9661f38 100644 (file)
@@ -1347,8 +1347,8 @@ class JoinedLoader(AbstractRelationshipLoader):
                 # name on it.
                 efm = inspect(adapter.aliased_class).\
                     _entity_for_mapper(
-                        parentmapper
-                        if parentmapper.isa(self.parent) else self.parent)
+                        localparent
+                        if localparent.isa(self.parent) else self.parent)
 
                 # look for our attribute on the adapted entity, else fall back
                 # to our straight property
index 01167b23e665e3e8b88692eefc02de95be7813ae..e8558237eecd9a066294a7dd36d03b36f0e7ab24 100644 (file)
@@ -1,6 +1,6 @@
 from sqlalchemy.orm import create_session, relationship, mapper, \
     contains_eager, joinedload, subqueryload, subqueryload_all,\
-    Session, aliased, with_polymorphic, joinedload_all
+    Session, aliased, with_polymorphic, joinedload_all, backref
 
 from sqlalchemy import Integer, String, ForeignKey
 from sqlalchemy.engine import default
@@ -1851,4 +1851,73 @@ class MultipleAdaptUsesEntityOverTableTest(AssertsCompiledSQL, fixtures.MappedTe
             "(a AS a_1 JOIN c AS c_1 ON a_1.id = c_1.id) ON c_1.bid = b.id "
             "JOIN (a AS a_2 JOIN d AS d_1 ON a_2.id = d_1.id) "
             "ON d_1.cid = c_1.id"
-        )
\ No newline at end of file
+        )
+
+
+class BetweenSubclassJoinWExtraJoinedLoad(
+        fixtures.DeclarativeMappedTest,
+        testing.AssertsCompiledSQL):
+    """test for [ticket:3884]"""
+
+    run_define_tables = None
+    __dialect__ = 'default'
+
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
+
+        class Person(Base):
+            __tablename__ = 'people'
+            id = Column(Integer, primary_key=True)
+            discriminator = Column('type', String(50))
+            __mapper_args__ = {'polymorphic_on': discriminator}
+
+        class Manager(Person):
+            __tablename__ = 'managers'
+            __mapper_args__ = {'polymorphic_identity': 'manager'}
+            id = Column(Integer, ForeignKey('people.id'), primary_key=True)
+
+        class Engineer(Person):
+            __tablename__ = 'engineers'
+            __mapper_args__ = {'polymorphic_identity': 'engineer'}
+            id = Column(Integer, ForeignKey('people.id'), primary_key=True)
+            primary_language = Column(String(50))
+            manager_id = Column(Integer, ForeignKey('managers.id'))
+            manager = relationship(
+                Manager, primaryjoin=(Manager.id == manager_id))
+
+        class LastSeen(Base):
+            __tablename__ = 'seen'
+            id = Column(Integer, ForeignKey('people.id'), primary_key=True)
+            timestamp = Column(Integer)
+            taggable = relationship(
+                Person, primaryjoin=(Person.id == id),
+                backref=backref("last_seen", lazy=False))
+
+    def test_query(self):
+        Engineer, Manager = self.classes("Engineer", "Manager")
+
+        sess = Session()
+
+        # eager join is both from Enginer->LastSeen as well as
+        # Manager->LastSeen.  In the case of Manager->LastSeen,
+        # Manager is internally aliased, and comes to JoinedEagerLoader
+        # with no "parent" entity but an adapter.
+        q = sess.query(Engineer, Manager).join(Engineer.manager)
+        self.assert_compile(
+            q,
+            "SELECT people.type AS people_type, engineers.id AS engineers_id, "
+            "people.id AS people_id, "
+            "engineers.primary_language AS engineers_primary_language, "
+            "engineers.manager_id AS engineers_manager_id, "
+            "people_1.type AS people_1_type, managers_1.id AS managers_1_id, "
+            "people_1.id AS people_1_id, seen_1.id AS seen_1_id, "
+            "seen_1.timestamp AS seen_1_timestamp, seen_2.id AS seen_2_id, "
+            "seen_2.timestamp AS seen_2_timestamp "
+            "FROM people JOIN engineers ON people.id = engineers.id "
+            "JOIN (people AS people_1 JOIN managers AS managers_1 "
+            "ON people_1.id = managers_1.id) "
+            "ON managers_1.id = engineers.manager_id LEFT OUTER JOIN "
+            "seen AS seen_1 ON people.id = seen_1.id LEFT OUTER JOIN "
+            "seen AS seen_2 ON people_1.id = seen_2.id"
+        )