diff --git a/CHANGES.txt b/CHANGES.txt index b956163..3037347 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,18 @@ Changes - Added Spanish locale: thanks to Douglas Cerna for the translations! +- If you use a schema with deferred ``validator``, ``missing`` or + ``default`` attributes, but you use it to perform serialization and + deserialization without calling its ``bind`` method: + + - If ``validator`` is deferred, no validation will be performed. + + - If ``missing`` is deferred, the field will be considered *required*. + + - If ``default`` is deferred, the serialization default will be + assumed to be ``colander.null``. + + 0.8 (2010/09/08) ----------------- diff --git a/colander/__init__.py b/colander/__init__.py index 174b334..37245fa 100644 --- a/colander/__init__.py +++ b/colander/__init__.py @@ -1220,6 +1220,8 @@ class SchemaNode(object): A return value of ``True`` implies that a ``missing`` value wasn't specified for this node. A return value of ``False`` implies that a ``missing`` value was specified for this node.""" + if isinstance(self.missing, deferred): # unbound schema with deferreds + return True return self.missing is _marker def serialize(self, appstruct=null): @@ -1236,6 +1238,8 @@ class SchemaNode(object): """ if appstruct is null: appstruct = self.default + if isinstance(appstruct, deferred): # unbound schema with deferreds + appstruct = null cstruct = self.typ.serialize(self, appstruct) return cstruct @@ -1262,12 +1266,15 @@ class SchemaNode(object): appstruct = self.missing if appstruct is _marker: raise Invalid(self, _('Required')) + if isinstance(appstruct, deferred): # unbound schema with deferreds + raise Invalid(self, _('Required')) # We never deserialize or validate the missing value return appstruct appstruct = self.typ.deserialize(self, cstruct) if self.validator is not None: - self.validator(self, appstruct) + if not isinstance(self.validator, deferred): # unbound + self.validator(self, appstruct) return appstruct def add(self, node): diff --git a/colander/tests.py b/colander/tests.py index c1f0d6d..3a3b7eb 100644 --- a/colander/tests.py +++ b/colander/tests.py @@ -1322,6 +1322,11 @@ class TestSchemaNode(unittest.TestCase): node = self._makeOne(None, missing=1) self.assertEqual(node.required, False) + def test_required_deferred(self): + from colander import deferred + node = self._makeOne(None, missing=deferred('123')) + self.assertEqual(node.required, True) + def test_deserialize_no_validator(self): typ = DummyType() node = self._makeOne(typ) @@ -1362,6 +1367,15 @@ class TestSchemaNode(unittest.TestCase): node.missing = null self.assertEqual(node.deserialize(null), null) + def test_deserialize_appstruct_deferred(self): + from colander import null + from colander import deferred + from colander import Invalid + typ = DummyType() + node = self._makeOne(typ) + node.missing = deferred('123') + self.assertRaises(Invalid, node.deserialize, null) + def test_serialize(self): typ = DummyType() node = self._makeOne(typ) @@ -1389,6 +1403,14 @@ class TestSchemaNode(unittest.TestCase): node.default = 'abc' self.assertEqual(node.serialize(), 'abc') + def test_serialize_default_deferred(self): + from colander import deferred + from colander import null + typ = DummyType() + node = self._makeOne(typ) + node.default = deferred('abc') + self.assertEqual(node.serialize(), null) + def test_add(self): node = self._makeOne(None) node.add(1) diff --git a/docs/binding.rst b/docs/binding.rst index 8a9fb17..b0c8e37 100644 --- a/docs/binding.rst +++ b/docs/binding.rst @@ -237,6 +237,20 @@ will be the set of keywords passed to the ``bind`` method. It usually operates on the ``node`` it is passed using the API methods described in :class:`SchemaNode`. +Unbound Schemas With Deferreds +------------------------------ + +If you use a schema with deferred ``validator``, ``missing`` or +``default`` attributes, but you use it to perform serialization and +deserialization without calling its ``bind`` method: + +- If ``validator`` is deferred, no validation will be performed. + +- If ``missing`` is deferred, the field will be considered *required*. + +- If ``default`` is deferred, the serialization default will be + assumed to be ``colander.null``. + See Also --------