summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--lib/sqlalchemy/ext/mutable.py10
-rw-r--r--test/ext/test_mutable.py27
3 files changed, 37 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index ec90b8bd8..e8e29e3e7 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 d14cfe3c0..078f9f3a2 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 236d711b2..ceff0d649 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 <type 'set'>",
+ 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()