]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed a regression in association proxy caused by :ticket:`2810` which
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 27 Feb 2014 18:29:59 +0000 (13:29 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 27 Feb 2014 18:29:59 +0000 (13:29 -0500)
caused a user-provided "getter" to no longer receive values of ``None``
when fetching scalar values from a target that is non-present.  The
check for None introduced by this change is now moved into the default
getter, so a user-provided getter will also again receive values of
None.
re: #2810

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/ext/associationproxy.py
test/ext/test_associationproxy.py

index a5c22aefc9acae488907723fb2a3702b5f15452c..637ef7d867e17c0ef8aeaae972823c4ff78f0869 100644 (file)
 .. changelog::
     :version: 0.9.4
 
+    .. change::
+        :tags: bug, ext
+        :tickets: 2810
+
+        Fixed a regression in association proxy caused by :ticket:`2810` which
+        caused a user-provided "getter" to no longer receive values of ``None``
+        when fetching scalar values from a target that is non-present.  The
+        check for None introduced by this change is now moved into the default
+        getter, so a user-provided getter will also again receive values of
+        None.
+
     .. change::
         :tags: bug, sql
         :tickets: 2974
index e62958b49f040a0114c05df81eb3b0c886660370..a4786de42d013bc6fb89751e5b36649542253594 100644 (file)
@@ -243,10 +243,7 @@ class AssociationProxy(interfaces._InspectionAttr):
 
         if self.scalar:
             target = getattr(obj, self.target_collection)
-            if target is not None:
-                return self._scalar_get(target)
-            else:
-                return None
+            return self._scalar_get(target)
         else:
             try:
                 # If the owning instance is reborn (orm session resurrect,
@@ -291,7 +288,8 @@ class AssociationProxy(interfaces._InspectionAttr):
 
     def _default_getset(self, collection_class):
         attr = self.value_attr
-        getter = operator.attrgetter(attr)
+        _getter = operator.attrgetter(attr)
+        getter = lambda target: _getter(target) if target is not None else None
         if collection_class is dict:
             setter = lambda o, k, v: setattr(o, attr, v)
         else:
index 3450eeb2f2ce817b858f01797e830964c6d90a89..48785060190d9aa82e52c9dd0486f922df5498f2 100644 (file)
@@ -12,6 +12,7 @@ from sqlalchemy.testing.util import gc_collect
 from sqlalchemy.testing import fixtures, AssertsCompiledSQL
 from sqlalchemy import testing
 from sqlalchemy.testing.schema import Table, Column
+from sqlalchemy.testing.mock import Mock, call
 
 class DictCollection(dict):
     @collection.appender
@@ -602,7 +603,6 @@ class CustomObjectTest(_CollectionOperations):
             p.children.__getitem__, 1
         )
 
-
 class ProxyFactoryTest(ListTest):
     def setup(self):
         metadata = MetaData(testing.db)
@@ -815,6 +815,36 @@ class ScalarTest(fixtures.TestBase):
         assert a1.a2b_name is None
         assert a1.b_single is None
 
+    def custom_getset_test(self):
+        metadata = MetaData()
+        p = Table('p', metadata,
+                              Column('id', Integer, primary_key=True),
+                              Column('cid', Integer, ForeignKey('c.id')))
+        c = Table('c', metadata,
+                               Column('id', Integer, primary_key=True),
+                               Column('foo', String(128)))
+
+        get = Mock()
+        set_ = Mock()
+        class Parent(object):
+            foo = association_proxy('child', 'foo',
+                    getset_factory=lambda cc, parent: (get, set_))
+
+        class Child(object):
+            def __init__(self, foo):
+                self.foo = foo
+
+        mapper(Parent, p, properties={'child': relationship(Child)})
+        mapper(Child, c)
+
+        p1 = Parent()
+
+        eq_(p1.foo, get(None))
+        p1.child = child = Child(foo='x')
+        eq_(p1.foo, get(child))
+        p1.foo = "y"
+        eq_(set_.mock_calls, [call(child, "y")])
+
 
 
 class LazyLoadTest(fixtures.TestBase):