]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed 1.0 regression where the "parent entity" of a synonym-
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 27 Jun 2015 04:40:34 +0000 (00:40 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 27 Jun 2015 04:40:34 +0000 (00:40 -0400)
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.

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/__init__.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/util.py
test/orm/test_query.py
test/orm/test_utils.py

index 9e21c9c7ae50038abb6f566abdce415c76c26ccd..e857445018f40adc3ef14dbd54607ed14172270d 100644 (file)
     .. include:: changelog_07.rst
         :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
index afddd59413dd4b57f83b3d803e139cd7af8b5210..093e90bbf20d42a6a168c6676de3ea02f2a4048f 100644 (file)
@@ -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):
index 6cc613baa73ad0aa5cba180620396024a7e84846..cd4a0116d086d7f318aed7156d55331a01430239 100644 (file)
@@ -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):
index 5694f72558cb3f9ba955e9efebdedf2665c1364c..55e02984b5b0d5ede30961567a1e020cb72fa5c8 100644 (file)
@@ -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})
index 66cb2a3198854d36d7668ea4e26d387d7b2616d7..6d3869679543802bf135d4158625c98dbbaf5f78 100644 (file)
@@ -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}
         )
 
index 62c97ec90d0a7102624f88ad7167e7080fe7c98f..55af023b1c1dff1fb2ec95dedba143b15783c0cb 100644 (file)
@@ -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'
index ae225ad9231a2e7f124520f9085dabe59f15245f..168cee19ca7b0a1f7a46287192e755472723c1f4 100644 (file)
@@ -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