]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed 1.0 regression where a "deferred" attribute would not populate
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 Jun 2015 17:47:27 +0000 (13:47 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 Jun 2015 17:49:44 +0000 (13:49 -0400)
correctly if it were loaded within the "optimized inheritance load",
which is a special SELECT emitted in the case of joined table
inheritance used to populate expired or unloaded attributes against
a joined table without loading the base table.  This is related to
the fact that SQLA 1.0 no longer guesses about loading deferred
columns and must be directed explicitly.
fixes #3468

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/orm/loading.py
test/orm/inheritance/test_basic.py

index e857445018f40adc3ef14dbd54607ed14172270d..9f0f0dff3ef629f04b7fd383b2e7226f3695c6c1 100644 (file)
 .. changelog::
     :version: 1.0.7
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 3468
+
+        Fixed 1.0 regression where a "deferred" attribute would not populate
+        correctly if it were loaded within the "optimized inheritance load",
+        which is a special SELECT emitted in the case of joined table
+        inheritance used to populate expired or unloaded attributes against
+        a joined table without loading the base table.  This is related to
+        the fact that SQLA 1.0 no longer guesses about loading deferred
+        columns and must be directed explicitly.
+
     .. change::
         :tags: bug, orm
         :tickets: 3466
index 50afaf6012bc3d371ad4622c9ba1a427c7fc819b..b81e98a580f5a3ff3bcab65a4e6d1978fab11d4e 100644 (file)
@@ -17,6 +17,8 @@ from __future__ import absolute_import
 from .. import util
 from . import attributes, exc as orm_exc
 from ..sql import util as sql_util
+from . import strategy_options
+
 from .util import _none_set, state_str
 from .base import _SET_DEFERRED_EXPIRED, _DEFER_FOR_STATE
 from .. import exc as sa_exc
@@ -612,10 +614,17 @@ def load_scalar_attributes(mapper, state, attribute_names):
     result = False
 
     if mapper.inherits and not mapper.concrete:
+        # because we are using Core to produce a select() that we
+        # pass to the Query, we aren't calling setup() for mapped
+        # attributes; in 1.0 this means deferred attrs won't get loaded
+        # by default
         statement = mapper._optimized_get_statement(state, attribute_names)
         if statement is not None:
             result = load_on_ident(
-                session.query(mapper).from_statement(statement),
+                session.query(mapper).
+                options(
+                    strategy_options.Load(mapper).undefer("*")
+                ).from_statement(statement),
                 None,
                 only_load_props=attribute_names,
                 refresh_state=state
index d8b2a44af023c25f28935c1076da17670af0cf89..911d4bc5c8879f6203693d8c7584e1ca0a6ac96a 100644 (file)
@@ -1148,6 +1148,62 @@ class FlushTest(fixtures.MappedTest):
         sess.flush()
         assert user_roles.count().scalar() == 1
 
+
+class OptimizedGetOnDeferredTest(fixtures.MappedTest):
+    """test that the 'optimized get' path accommodates deferred columns."""
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "a", metadata,
+            Column('id', Integer, primary_key=True,
+                   test_needs_autoincrement=True)
+        )
+        Table(
+            "b", metadata,
+            Column('id', Integer, ForeignKey('a.id'), primary_key=True),
+            Column('data', String(10))
+        )
+
+    @classmethod
+    def setup_classes(cls):
+        class A(cls.Basic):
+            pass
+
+        class B(A):
+            pass
+
+    @classmethod
+    def setup_mappers(cls):
+        A, B = cls.classes("A", "B")
+        a, b = cls.tables("a", "b")
+
+        mapper(A, a)
+        mapper(B, b, inherits=A, properties={
+            'data': deferred(b.c.data),
+            'expr': column_property(b.c.data + 'q', deferred=True)
+        })
+
+    def test_column_property(self):
+        A, B = self.classes("A", "B")
+        sess = Session()
+        b1 = B(data='x')
+        sess.add(b1)
+        sess.flush()
+
+        eq_(b1.expr, 'xq')
+
+    def test_expired_column(self):
+        A, B = self.classes("A", "B")
+        sess = Session()
+        b1 = B(data='x')
+        sess.add(b1)
+        sess.flush()
+        sess.expire(b1, ['data'])
+
+        eq_(b1.data, 'x')
+
+
 class JoinedNoFKSortingTest(fixtures.MappedTest):
     @classmethod
     def define_tables(cls, metadata):