: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
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,
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')
p.child = None
- # No child again
- assert_raises(
- AttributeError,
- getattr, p, "foo"
- )
+ eq_(p.foo, None)
# Bogus creator for this scalar type
assert_raises(
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):