diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-10-25 17:56:53 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-10-25 17:56:53 -0400 |
commit | 91ae63569df12654e0eae576938066a4079439aa (patch) | |
tree | f03f5155a9697eb2029af83e50f2c6a1afcfa57f | |
parent | b0cd41dd50e9279b8f7823eadd001f04605990a4 (diff) | |
download | sqlalchemy-91ae63569df12654e0eae576938066a4079439aa.tar.gz |
- catch the metadata on ScalarTest.test_scalar_proxy, this has been leaving itself
around for a long time
- association proxy now returns None for proxied scalar that is also None, rather
than raising AttributeError. [ticket:2810]
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 12 | ||||
-rw-r--r-- | doc/build/changelog/migration_09.rst | 43 | ||||
-rw-r--r-- | doc/build/orm/extensions/associationproxy.rst | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/ext/associationproxy.py | 6 | ||||
-rw-r--r-- | test/ext/test_associationproxy.py | 58 |
5 files changed, 108 insertions, 12 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index d281397b4..4c9caf1f8 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -13,6 +13,18 @@ :version: 0.9.0 .. change:: + :tags: feature, orm + :tickets: 2810 + + The association proxy now returns ``None`` when fetching a scalar + attribute off of a scalar relationship, where the scalar relationship + itself points to ``None``, instead of raising an ``AttributeError``. + + .. seealso:: + + :ref:`migration_2810` + + .. change:: :tags: feature, sql, postgresql, mysql :tickets: 2183 diff --git a/doc/build/changelog/migration_09.rst b/doc/build/changelog/migration_09.rst index 3aaf9670b..936097328 100644 --- a/doc/build/changelog/migration_09.rst +++ b/doc/build/changelog/migration_09.rst @@ -321,6 +321,49 @@ against ``b_value`` directly. :ticket:`2751` +.. _migration_2810: + +Association Proxy Missing Scalar returns None +--------------------------------------------- + +An association proxy from a scalar attribute to a scalar will now return +``None`` if the proxied object isn't present. This is consistent with the +fact that missing many-to-ones return None in SQLAlchemy, so should the +proxied value. E.g.:: + + from sqlalchemy import * + from sqlalchemy.orm import * + from sqlalchemy.ext.declarative import declarative_base + from sqlalchemy.ext.associationproxy import association_proxy + + Base = declarative_base() + + class A(Base): + __tablename__ = 'a' + + id = Column(Integer, primary_key=True) + b = relationship("B", uselist=False) + + bname = association_proxy("b", "name") + + class B(Base): + __tablename__ = 'b' + + id = Column(Integer, primary_key=True) + a_id = Column(Integer, ForeignKey('a.id')) + name = Column(String) + + a1 = A() + + # this is how m2o's always have worked + assert a1.b is None + + # but prior to 0.9, this would raise AttributeError, + # now returns None just like the proxied value. + assert a1.bname is None + +:ticket:`2810` + .. _migration_2850: A bindparam() construct with no type gets upgraded via copy when a type is available diff --git a/doc/build/orm/extensions/associationproxy.rst b/doc/build/orm/extensions/associationproxy.rst index 90bb29ebf..9b25c4a68 100644 --- a/doc/build/orm/extensions/associationproxy.rst +++ b/doc/build/orm/extensions/associationproxy.rst @@ -15,6 +15,7 @@ the construction of sophisticated collections and dictionary views of virtually any geometry, persisted to the database using standard, transparently configured relational patterns. + Simplifying Scalar Collections ------------------------------ diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index fca2f0008..60875bcf0 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -242,7 +242,11 @@ class AssociationProxy(interfaces._InspectionAttr): return self if self.scalar: - return self._scalar_get(getattr(obj, self.target_collection)) + target = getattr(obj, self.target_collection) + if target is not None: + return self._scalar_get(target) + else: + return None else: try: # If the owning instance is reborn (orm session resurrect, diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index d46b08f6d..3450eeb2f 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -666,8 +666,9 @@ class ProxyFactoryTest(ListTest): class ScalarTest(fixtures.TestBase): + @testing.provide_metadata def test_scalar_proxy(self): - metadata = MetaData(testing.db) + metadata = self.metadata parents_table = Table('Parent', metadata, Column('id', Integer, primary_key=True, @@ -715,11 +716,8 @@ class ScalarTest(fixtures.TestBase): p = Parent('p') - # No child - assert_raises( - AttributeError, - getattr, p, "foo" - ) + eq_(p.child, None) + eq_(p.foo, None) p.child = Child(foo='a', bar='b', baz='c') @@ -740,11 +738,7 @@ class ScalarTest(fixtures.TestBase): p.child = None - # No child again - assert_raises( - AttributeError, - getattr, p, "foo" - ) + eq_(p.foo, None) # Bogus creator for this scalar type assert_raises( @@ -780,6 +774,48 @@ class ScalarTest(fixtures.TestBase): p2 = Parent('p2') p2.bar = 'quux' + @testing.provide_metadata + def test_empty_scalars(self): + metadata = self.metadata + + a = Table('a', metadata, + Column('id', Integer, primary_key=True), + Column('name', String(50)) + ) + a2b = Table('a2b', metadata, + Column('id', Integer, primary_key=True), + Column('id_a', Integer, ForeignKey('a.id')), + Column('id_b', Integer, ForeignKey('b.id')), + Column('name', String(50)) + ) + b = Table('b', metadata, + Column('id', Integer, primary_key=True), + Column('name', String(50)) + ) + class A(object): + a2b_name = association_proxy("a2b_single", "name") + b_single = association_proxy("a2b_single", "b") + + class A2B(object): + pass + + class B(object): + pass + + mapper(A, a, properties=dict( + a2b_single=relationship(A2B, uselist=False) + )) + + mapper(A2B, a2b, properties=dict( + b=relationship(B) + )) + mapper(B, b) + + a1 = A() + assert a1.a2b_name is None + assert a1.b_single is None + + class LazyLoadTest(fixtures.TestBase): def setup(self): |