diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-27 00:40:34 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-27 00:40:34 -0400 |
commit | fcb7c784e9479b9bff7de20c41a05bc1aa550ffb (patch) | |
tree | 31069380bd348fce6b2b71d6e5330fe65f231e6f | |
parent | 012b3bd0b2a0e9cdfd11d797b6aa0053a13816b0 (diff) | |
download | sqlalchemy-fcb7c784e9479b9bff7de20c41a05bc1aa550ffb.tar.gz |
- Fixed 1.0 regression where the "parent entity" of a synonym-
mapped attribute on top of an :func:`.aliased` object would
resolve to the original mapper, not the :func:`.aliased`
version of it, thereby causing problems for a :class:`.Query`
that relies on this attribute (e.g. it's the only representative
attribute given in the constructor) to figure out the correct FROM
clause for the query.
fixes #3466
- apply consitency to ._parententity vs.
__clause_element__()._annotations['parententity']
in terms of aliased class, test it all.
-rw-r--r-- | doc/build/changelog/changelog_10.rst | 15 | ||||
-rw-r--r-- | lib/sqlalchemy/__init__.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/properties.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/util.py | 2 | ||||
-rw-r--r-- | test/orm/test_query.py | 17 | ||||
-rw-r--r-- | test/orm/test_utils.py | 50 |
7 files changed, 86 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 9e21c9c7a..e85744501 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -16,6 +16,21 @@ :start-line: 5 .. changelog:: + :version: 1.0.7 + + .. change:: + :tags: bug, orm + :tickets: 3466 + + Fixed 1.0 regression where the "parent entity" of a synonym- + mapped attribute on top of an :func:`.aliased` object would + resolve to the original mapper, not the :func:`.aliased` + version of it, thereby causing problems for a :class:`.Query` + that relies on this attribute (e.g. it's the only representative + attribute given in the constructor) to figure out the correct FROM + clause for the query. + +.. changelog:: :version: 1.0.6 :released: June 25, 2015 diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py index afddd5941..093e90bbf 100644 --- a/lib/sqlalchemy/__init__.py +++ b/lib/sqlalchemy/__init__.py @@ -120,7 +120,7 @@ from .schema import ( from .inspection import inspect from .engine import create_engine, engine_from_config -__version__ = '1.0.6' +__version__ = '1.0.7' def __go(lcls): diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 6cc613baa..cd4a0116d 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -338,7 +338,7 @@ class PropComparator(operators.ColumnOperators): def __init__(self, prop, parentmapper, adapt_to_entity=None): self.prop = self.property = prop - self._parententity = parentmapper + self._parententity = adapt_to_entity or parentmapper self._adapt_to_entity = adapt_to_entity def __clause_element__(self): diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 5694f7255..55e02984b 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -245,6 +245,8 @@ class ColumnProperty(StrategizedProperty): if self.adapter: return self.adapter(self.prop.columns[0]) else: + # no adapter, so we aren't aliased + # assert self._parententity is self._parentmapper return self.prop.columns[0]._annotate({ "parententity": self._parententity, "parentmapper": self._parententity}) diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 66cb2a319..6d3869679 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -530,7 +530,7 @@ class AliasedInsp(InspectionAttr): def _adapt_element(self, elem): return self._adapter.traverse(elem).\ _annotate({ - 'parententity': self.entity, + 'parententity': self, 'parentmapper': self.mapper} ) diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 62c97ec90..55af023b1 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -3390,7 +3390,8 @@ class WithTransientOnNone(_fixtures.FixtureTest, AssertsCompiledSQL): ) -class SynonymTest(QueryTest): +class SynonymTest(QueryTest, AssertsCompiledSQL): + __dialect__ = 'default' @classmethod def setup_mappers(cls): @@ -3510,6 +3511,20 @@ class SynonymTest(QueryTest): Order(description="order 1"), Order(description="order 3"), Order(description="order 5")] == o + def test_froms_aliased_col(self): + Address, User = self.classes.Address, self.classes.User + + sess = create_session() + ua = aliased(User) + + q = sess.query(ua.name_syn).join( + Address, ua.id == Address.user_id) + self.assert_compile( + q, + "SELECT users_1.name AS users_1_name FROM " + "users AS users_1 JOIN addresses ON users_1.id = addresses.user_id" + ) + class ImmediateTest(_fixtures.FixtureTest): run_inserts = 'once' diff --git a/test/orm/test_utils.py b/test/orm/test_utils.py index ae225ad92..168cee19c 100644 --- a/test/orm/test_utils.py +++ b/test/orm/test_utils.py @@ -222,6 +222,56 @@ class AliasedClassTest(fixtures.TestBase, AssertsCompiledSQL): "WHERE point_1.x > point.x" ) + def test_parententity_vs_parentmapper(self): + class Point(object): + pass + + self._fixture(Point, properties={ + 'x_syn': synonym("x") + }) + pa = aliased(Point) + + is_(Point.x_syn._parententity, inspect(Point)) + is_(Point.x._parententity, inspect(Point)) + is_(Point.x_syn._parentmapper, inspect(Point)) + is_(Point.x._parentmapper, inspect(Point)) + + is_( + Point.x_syn.__clause_element__()._annotations['parententity'], + inspect(Point)) + is_( + Point.x.__clause_element__()._annotations['parententity'], + inspect(Point)) + is_( + Point.x_syn.__clause_element__()._annotations['parentmapper'], + inspect(Point)) + is_( + Point.x.__clause_element__()._annotations['parentmapper'], + inspect(Point)) + + pa = aliased(Point) + + is_(pa.x_syn._parententity, inspect(pa)) + is_(pa.x._parententity, inspect(pa)) + is_(pa.x_syn._parentmapper, inspect(Point)) + is_(pa.x._parentmapper, inspect(Point)) + + is_( + pa.x_syn.__clause_element__()._annotations['parententity'], + inspect(pa) + ) + is_( + pa.x.__clause_element__()._annotations['parententity'], + inspect(pa) + ) + is_( + pa.x_syn.__clause_element__()._annotations['parentmapper'], + inspect(Point)) + is_( + pa.x.__clause_element__()._annotations['parentmapper'], + inspect(Point)) + + class IdentityKeyTest(_fixtures.FixtureTest): run_inserts = None |