From 501b8aec1b2ccd3cb652cd6f7f8d00047ef5e84c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 14 May 2011 10:23:12 -0400 Subject: [PATCH] - Fixed bugs in sqlalchemy.ext.mutable extension where `None` was not appropriately handled, replacement events were not appropriately handled. [ticket:2143] --- CHANGES | 6 ++++++ lib/sqlalchemy/ext/mutable.py | 10 +++++----- test/ext/test_mutable.py | 27 ++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index ec90b8bd87..e8e29e3e79 100644 --- a/CHANGES +++ b/CHANGES @@ -132,6 +132,12 @@ CHANGES "generic_associations". Each presents an alternative table layout. +- ext + - Fixed bugs in sqlalchemy.ext.mutable extension where + `None` was not appropriately handled, replacement + events were not appropriately handled. + [ticket:2143] + 0.7.0b4 ======= - general diff --git a/lib/sqlalchemy/ext/mutable.py b/lib/sqlalchemy/ext/mutable.py index d14cfe3c0f..078f9f3a2d 100644 --- a/lib/sqlalchemy/ext/mutable.py +++ b/lib/sqlalchemy/ext/mutable.py @@ -378,12 +378,12 @@ class MutableBase(object): outgoing. """ - if not isinstance(value, cls): - value = cls.coerce(key, value) - value._parents[target.obj()] = key + value = cls.coerce(key, value) + if value is not None: + value._parents[target.obj()] = key if isinstance(oldvalue, cls): - oldvalue._parents.pop(state.obj(), None) + oldvalue._parents.pop(target.obj(), None) return value def pickle(state, state_dict): @@ -426,7 +426,7 @@ class Mutable(MutableBase): """ if value is None: return None - raise ValueError("Attribute '%s' accepts objects of type %s" % (key, cls)) + raise ValueError("Attribute '%s' does not accept objects of type %s" % (key, type(value))) @classmethod def associate_with_attribute(cls, attribute): diff --git a/test/ext/test_mutable.py b/test/ext/test_mutable.py index 236d711b2a..ceff0d6499 100644 --- a/test/ext/test_mutable.py +++ b/test/ext/test_mutable.py @@ -4,7 +4,7 @@ from sqlalchemy.orm import mapper, Session, composite from sqlalchemy.orm.mapper import Mapper from sqlalchemy.orm.instrumentation import ClassManager from test.lib.schema import Table, Column -from test.lib.testing import eq_ +from test.lib.testing import eq_, assert_raises_message from test.lib.util import picklers from test.lib import testing from test.lib import fixtures @@ -60,6 +60,21 @@ class _MutableDictTestBase(object): ClassManager.dispatch._clear() super(_MutableDictTestBase, self).teardown() + def test_coerce_none(self): + sess = Session() + f1 = Foo(data=None) + sess.add(f1) + sess.commit() + eq_(f1.data, None) + + def test_coerce_raise(self): + assert_raises_message( + ValueError, + "Attribute 'data' does not accept objects of " + "type ", + Foo, data=set([1,2,3]) + ) + def test_in_place_mutation(self): sess = Session() @@ -72,6 +87,16 @@ class _MutableDictTestBase(object): eq_(f1.data, {'a':'c'}) + def test_replace(self): + sess = Session() + f1 = Foo(data={'a':'b'}) + sess.add(f1) + sess.flush() + + f1.data = {'b':'c'} + sess.commit() + eq_(f1.data, {'b':'c'}) + def test_pickle_parent(self): sess = Session() -- 2.39.5