Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Feature: 'default' and missing 'missing' of SchemaNode can be callables. #62

Closed
wants to merge 2 commits into from

2 participants

@stefanofontanelli

This commit allows the following code:

import colander
import datetime

class Log(colander.MappingSchema):
    service_name = colander.SchemaNode(colander.String())
    action = colander.SchemaNode(colander.String())
    timestamp = colander.SchemaNode(colander.DateTime(),
                                                         default=datetime.datetime.now,
                                                         missing=datetime.datetime.now)

Log.default and Log.missing are not statically assigned, instead they will be "generated" at serialization/deserialization time.

@kiorky
Collaborator

This can be done by using deferred, please look at http://docs.pylonsproject.org/projects/colander/en/latest/binding.html

@kiorky kiorky closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 13, 2012
  1. @stefanofontanelli

    Add support for callables in 'default'/'missing' serialization/deseri…

    stefanofontanelli authored
    …alization.
    
    Add tests.
    Change docs inside SchemaNode.__init__.
Commits on Aug 14, 2012
  1. @stefanofontanelli
This page is out of date. Refresh to see the latest.
Showing with 49 additions and 5 deletions.
  1. +11 −4 colander/__init__.py
  2. +38 −1 colander/tests/test_colander.py
View
15 colander/__init__.py
@@ -1444,12 +1444,14 @@ class SchemaNode(object):
- ``default``: The default serialization value for this node.
Default: :attr:`colander.null`.
+ It can also be any python callable: it will be called during serialization.
- - ``missing``: The default deserialization value for this node. If it is
+ - ``missing``: The default deserialization value for this node. If it is
not provided, the missing value of this node will be the special marker
value :attr:`colander.required`, indicating that it is considered
'required'. When ``missing`` is :attr:`colander.required`, the
``required`` computed attribute will be ``True``.
+ It can also be any python callable: it will be called during deserialization.
- ``preparer``: Optional preparer for this node. It should be
an object that implements the
@@ -1540,9 +1542,12 @@ def serialize(self, appstruct=null):
If an ``appstruct`` argument is not explicitly provided, it
defaults to :attr:`colander.null`.
"""
- if appstruct is null:
+ if appstruct is null and\
+ not isinstance(self.default, deferred) and callable(self.default):
+ appstruct = self.default()
+ elif appstruct is null:
appstruct = self.default
- if isinstance(appstruct, deferred): # unbound schema with deferreds
+ if isinstance(appstruct, deferred): # unbound schema with deferreds
appstruct = null
cstruct = self.typ.serialize(self, appstruct)
return cstruct
@@ -1606,9 +1611,11 @@ def deserialize(self, cstruct=null):
if appstruct is null:
appstruct = self.missing
+ if not isinstance(appstruct, deferred) and callable(appstruct):
+ appstruct = appstruct()
if appstruct is required:
raise Invalid(self, _('Required'))
- if isinstance(appstruct, deferred): # unbound schema with deferreds
+ if isinstance(appstruct, deferred): # unbound schema with deferreds
raise Invalid(self, _('Required'))
# We never deserialize or validate the missing value
return appstruct
View
39 colander/tests/test_colander.py
@@ -2174,7 +2174,44 @@ class FnordSchema(colander.Schema):
)
schema = FnordSchema()
self.assertEqual(schema['fnord[]'].name, 'fnord[]')
-
+
+ def test_deserialize_missing_callable(self):
+ import datetime
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.missing = datetime.datetime.now
+ data = node.deserialize()
+ self.assertEqual(isinstance(data, datetime.datetime), True)
+
+ def test_deserialize_wrong_missing_callable(self):
+
+ def f(a):
+ return {}
+
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.missing = f
+ self.assertRaises(TypeError, node.deserialize)
+
+ def test_serialize_default_callable(self):
+ import datetime
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.default = datetime.datetime.now
+ data = node.serialize()
+ self.assertEqual(isinstance(data, datetime.datetime), True)
+
+ def test_serialize_wrong_default_callable(self):
+
+ def f(a):
+ return {}
+
+ typ = DummyType()
+ node = self._makeOne(typ)
+ node.default = f
+ self.assertRaises(TypeError, node.serialize)
+
+
class TestDeferred(unittest.TestCase):
def _makeOne(self, wrapped):
from colander import deferred
Something went wrong with that request. Please try again.