summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-01-09 14:16:22 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2017-01-09 14:16:22 -0500
commit51a72503b0279ca71ee6f0454bfd36a4c84d508f (patch)
treee912d80c832d98ff9b5eb8d5ba499b1c15eb7791
parent2b4d028a69270c1c7918281a60280dd0b65963a2 (diff)
downloadsqlalchemy-51a72503b0279ca71ee6f0454bfd36a4c84d508f.tar.gz
Adapt from "localparent" in joinedloader
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
-rw-r--r--doc/build/changelog/changelog_10.rst13
-rw-r--r--lib/sqlalchemy/orm/strategies.py4
-rw-r--r--test/orm/inheritance/test_relationship.py72
3 files changed, 86 insertions, 3 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 4ae8a8fbb..aa3a4c7e6 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -16,6 +16,19 @@
: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
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 33feab0dc..273ec1280 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -1368,8 +1368,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
diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py
index 46b934d7b..9f58896af 100644
--- a/test/orm/inheritance/test_relationship.py
+++ b/test/orm/inheritance/test_relationship.py
@@ -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, select, func
from sqlalchemy.engine import default
@@ -1934,3 +1934,73 @@ class SameNameOnJoined(fixtures.MappedTest):
s.commit()
eq_(s.query(B).count(), 0)
+
+
+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"
+ )
+