]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Ensure mapper.polymorphic_on is polymorphic_prop.columns[0]
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 21 Sep 2016 21:55:39 +0000 (17:55 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 21 Sep 2016 21:57:38 +0000 (17:57 -0400)
Fixed bug where joined eager loading would fail for a polymorphically-
loaded mapper, where the polymorphic_on was set to an un-mapped
expression such as a CASE expression.

Change-Id: Iffe68196aaac592165c89684f09f4c06cd78ce54
Fixes: #3800
doc/build/changelog/changelog_10.rst
lib/sqlalchemy/orm/mapper.py
test/orm/inheritance/test_basic.py

index 8808f6511c8d064fffa3949870419c305dc53b26..b3e76b4756611fc982e92ae685e12f6a2b231498 100644 (file)
 .. changelog::
     :version: 1.0.16
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 3800
+        :versions: 1.1.0
+
+        Fixed bug where joined eager loading would fail for a polymorphically-
+        loaded mapper, where the polymorphic_on was set to an un-mapped
+        expression such as a CASE expression.
+
     .. change::
         :tags: bug, orm
         :tickets: 3798
index b8dc5b8c376df99adc03e6cee82f79b369237906..60848097c65b8b65df7418f864a95417c41c478c 100644 (file)
@@ -1401,9 +1401,6 @@ class Mapper(InspectionAttr):
                 # polymorphic_on is a column that is already mapped
                 # to a ColumnProperty
                 prop = self._columntoproperty[self.polymorphic_on]
-                polymorphic_key = prop.key
-                self.polymorphic_on = prop.columns[0]
-                polymorphic_key = prop.key
             elif isinstance(self.polymorphic_on, MapperProperty):
                 # polymorphic_on is directly a MapperProperty,
                 # ensure it's a ColumnProperty
@@ -1414,8 +1411,6 @@ class Mapper(InspectionAttr):
                         "property or SQL expression "
                         "can be passed for polymorphic_on")
                 prop = self.polymorphic_on
-                self.polymorphic_on = prop.columns[0]
-                polymorphic_key = prop.key
             elif not expression._is_column(self.polymorphic_on):
                 # polymorphic_on is not a Column and not a ColumnProperty;
                 # not supported right now.
@@ -1477,12 +1472,14 @@ class Mapper(InspectionAttr):
                         col.label("_sa_polymorphic_on")
                     key = col.key
 
-                self._configure_property(
-                    key,
-                    properties.ColumnProperty(col,
-                                              _instrument=instrument),
-                    init=init, setparent=True)
-                polymorphic_key = key
+                prop = properties.ColumnProperty(col, _instrument=instrument)
+                self._configure_property(key, prop, init=init, setparent=True)
+
+            # the actual polymorphic_on should be the first public-facing
+            # column in the property
+            self.polymorphic_on = prop.columns[0]
+            polymorphic_key = prop.key
+
         else:
             # no polymorphic_on was set.
             # check inheriting mappers for one.
index 42959a6e3694203c810160e9189d6aef89924f1c..7b5f85b9b674b3b7f5d7321c36dc116f57d8818b 100644 (file)
@@ -1,5 +1,5 @@
 import warnings
-from sqlalchemy.testing import eq_, assert_raises, assert_raises_message
+from sqlalchemy.testing import eq_, is_, assert_raises, assert_raises_message
 from sqlalchemy import *
 from sqlalchemy import exc as sa_exc, util, event
 from sqlalchemy.orm import *
@@ -77,6 +77,65 @@ class O2MTest(fixtures.MappedTest):
         eq_(l[0].parent_foo.data, 'foo #1')
         eq_(l[1].parent_foo.data, 'foo #1')
 
+
+class PolyExpressionEagerLoad(fixtures.DeclarativeMappedTest):
+    run_setup_mappers = 'once'
+    __dialect__ = 'default'
+
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
+
+        class A(fixtures.ComparableEntity, Base):
+            __tablename__ = 'a'
+
+            id = Column(Integer, primary_key=True,
+                        test_needs_autoincrement=True)
+            discriminator = Column(String(50), nullable=False)
+            child_id = Column(Integer, ForeignKey('a.id'))
+            child = relationship('A')
+
+            p_a = case([
+                (discriminator == "a", "a"),
+            ], else_="b")
+
+            __mapper_args__ = {
+                'polymorphic_identity': 'a',
+                "polymorphic_on": p_a,
+            }
+
+        class B(A):
+            __mapper_args__ = {
+                'polymorphic_identity': 'b'
+            }
+
+    @classmethod
+    def insert_data(cls):
+        A = cls.classes.A
+
+        session = Session(testing.db)
+        session.add_all([
+            A(id=1, discriminator='a'),
+            A(id=2, discriminator='b', child_id=1),
+            A(id=3, discriminator='c', child_id=1),
+        ])
+        session.commit()
+
+    def test_joinedload(self):
+        A = self.classes.A
+        B = self.classes.B
+
+        session = Session(testing.db)
+        result = session.query(A).filter_by(child_id=None).\
+            options(joinedload('child')).one()
+
+
+        eq_(
+            result,
+            A(id=1, discriminator='a', child=[B(id=2), B(id=3)]),
+        )
+
+
 class PolymorphicResolutionMultiLevel(fixtures.DeclarativeMappedTest,
                                         testing.AssertsCompiledSQL):
     run_setup_mappers = 'once'
@@ -396,6 +455,22 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest):
     def _roundtrip(self, set_event=True, parent_ident='parent', child_ident='child'):
         Parent, Child = self.classes.Parent, self.classes.Child
 
+        # locate the "polymorphic_on" ColumnProperty.   This isn't
+        # "officially" stored at the moment so do some heuristics to find it.
+        parent_mapper = inspect(Parent)
+        for prop in parent_mapper.column_attrs:
+            if not prop.instrument:
+                break
+        else:
+            prop = parent_mapper._columntoproperty[
+                parent_mapper.polymorphic_on]
+
+        # then make sure the column we will query on matches.
+        is_(
+            parent_mapper.polymorphic_on,
+            prop.columns[0]
+        )
+
         if set_event:
             @event.listens_for(Parent, "init", propagate=True)
             def set_identity(instance, *arg, **kw):