diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-02-27 13:29:59 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-02-27 13:29:59 -0500 |
commit | b6428219c663e0eb0bf44817391d7a8fc1c60d43 (patch) | |
tree | e7b233e5ba80ba199035299e472f754393d350b9 | |
parent | 2d146ddad97e637c399d274c845866d2e21e9b93 (diff) | |
download | sqlalchemy-b6428219c663e0eb0bf44817391d7a8fc1c60d43.tar.gz |
- 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.
re: #2810
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/ext/associationproxy.py | 8 | ||||
-rw-r--r-- | test/ext/test_associationproxy.py | 32 |
3 files changed, 45 insertions, 6 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index a5c22aefc..637ef7d86 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,6 +15,17 @@ :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 diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index e62958b49..a4786de42 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -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: diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index 3450eeb2f..487850601 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -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): |