]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Resolve AliasedClass when determining owning class of association proxy
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 20 Oct 2017 18:34:16 +0000 (14:34 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 24 Oct 2017 00:36:18 +0000 (20:36 -0400)
Fixed bug where the association proxy would inadvertently link itself
to an :class:`.AliasedClass` object if it were called first with
the :class:`.AliasedClass` as a parent, causing errors upon subsequent
usage.

Change-Id: I9161bab67766bb75d73ca54d712ad1cad6de40dc
Fixes: #4116
(cherry picked from commit f14a58dea4b825beb4baaef44389880927543cc4)

doc/build/changelog/unreleased_11/4116.rst [new file with mode: 0644]
lib/sqlalchemy/ext/associationproxy.py
test/ext/test_associationproxy.py

diff --git a/doc/build/changelog/unreleased_11/4116.rst b/doc/build/changelog/unreleased_11/4116.rst
new file mode 100644 (file)
index 0000000..f4cf1f4
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, orm, ext
+    :tickets: 4116
+    :versions: 1.2.0b4
+
+    Fixed bug where the association proxy would inadvertently link itself
+    to an :class:`.AliasedClass` object if it were called first with
+    the :class:`.AliasedClass` as a parent, causing errors upon subsequent
+    usage.
\ No newline at end of file
index 6f570a1fa955154a069bf69cb3a4eb168de6890f..80078495571d0f9848a7e7452b22ce17d6583c22 100644 (file)
@@ -19,6 +19,7 @@ import weakref
 from .. import exc, orm, util
 from ..orm import collections, interfaces
 from ..sql import not_, or_
+from .. import inspect
 
 
 def association_proxy(target_collection, attr, **kw):
@@ -245,7 +246,17 @@ class AssociationProxy(interfaces.InspectionAttrInfo):
 
     def __get__(self, obj, class_):
         if self.owning_class is None:
-            self.owning_class = class_ and class_ or type(obj)
+            try:
+                insp = inspect(class_)
+            except exc.NoInspectionAvailable:
+                pass
+            else:
+                if hasattr(insp, 'mapper'):
+                    self.owning_class = insp.mapper.class_
+
+            if self.owning_class is None:
+                self.owning_class = type(obj)
+
         if obj is None:
             return self
 
index 0f86a9a14ebdb5987f2c0c867a8a6da37c47b245..3ef09599cbec704f4c5791ead3707a85f1c58ded 100644 (file)
@@ -1,4 +1,4 @@
-from sqlalchemy.testing import eq_, assert_raises
+from sqlalchemy.testing import eq_, assert_raises, is_
 import copy
 import pickle
 
@@ -14,6 +14,7 @@ from sqlalchemy import testing
 from sqlalchemy.testing.schema import Table, Column
 from sqlalchemy.testing.mock import Mock, call
 from sqlalchemy.testing.assertions import expect_warnings
+from sqlalchemy.ext.declarative import declarative_base
 
 
 class DictCollection(dict):
@@ -1574,6 +1575,31 @@ class DictOfTupleUpdateTest(fixtures.TestBase):
         )
 
 
+class AttributeAccessTest(fixtures.TestBase):
+    def test_resolve_aliased_class(self):
+        Base = declarative_base()
+
+        class A(Base):
+            __tablename__ = 'a'
+            id = Column(Integer, primary_key=True)
+            value = Column(String)
+
+        class B(Base):
+            __tablename__ = 'b'
+            id = Column(Integer, primary_key=True)
+            a_id = Column(Integer, ForeignKey(A.id))
+            a = relationship(A)
+            a_value = association_proxy('a', 'value')
+
+        spec = aliased(B).a_value
+
+        is_(spec.owning_class, B)
+
+        spec = B.a_value
+
+        is_(spec.owning_class, B)
+
+
 class InfoTest(fixtures.TestBase):
     def test_constructor(self):
         assoc = association_proxy('a', 'b', info={'some_assoc': 'some_value'})