diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-29 13:47:27 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-29 13:49:44 -0400 |
commit | 4d6f4ed184b94e60d5d39eff7fae678d64e9aeaa (patch) | |
tree | 097988c3a6c6f3c3717f741ca963dc678a6f8aa5 | |
parent | fcb7c784e9479b9bff7de20c41a05bc1aa550ffb (diff) | |
download | sqlalchemy-4d6f4ed184b94e60d5d39eff7fae678d64e9aeaa.tar.gz |
- 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.
fixes #3468
-rw-r--r-- | doc/build/changelog/changelog_10.rst | 12 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/loading.py | 11 | ||||
-rw-r--r-- | test/orm/inheritance/test_basic.py | 56 |
3 files changed, 78 insertions, 1 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index e85744501..9f0f0dff3 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -20,6 +20,18 @@ .. 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 Fixed 1.0 regression where the "parent entity" of a synonym- diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index 50afaf601..b81e98a58 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -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 diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index d8b2a44af..911d4bc5c 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -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): |