implement, document and test the 'instantiate' decorator
This commit is contained in:
@@ -6,6 +6,9 @@ Features
|
||||
|
||||
- Add ``colander.ContainsOnly`` and ``colander.url`` validators.
|
||||
|
||||
- Add ``colander.instantiate`` to help define schemas containing
|
||||
mappings and sequences more succinctly.
|
||||
|
||||
1.0a1 (2013-01-10)
|
||||
------------------
|
||||
|
||||
|
@@ -2089,3 +2089,18 @@ def _unflatten_mapping(node, paths, fstruct,
|
||||
appstruct[curname] = subnode.typ.unflatten(
|
||||
subnode, subpaths, subfstruct)
|
||||
return appstruct
|
||||
|
||||
class instantiate(object):
|
||||
"""
|
||||
A decorator which can be used to instantiate :class:`SchemaNode`
|
||||
elements inline within a class definition.
|
||||
|
||||
All parameters passed to the decorator and passed along to the
|
||||
:class:`SchemaNode` during instantiation.
|
||||
"""
|
||||
|
||||
def __init__(self,*args,**kw):
|
||||
self.args,self.kw = args,kw
|
||||
|
||||
def __call__(self,class_):
|
||||
return class_(*self.args,**self.kw)
|
||||
|
@@ -3337,6 +3337,42 @@ class TestUltraDeclarative(unittest.TestCase, TestFunctional):
|
||||
schema = MainSchema()
|
||||
return schema
|
||||
|
||||
class TestDeclarativeWithInstantiate(unittest.TestCase, TestFunctional):
|
||||
|
||||
def _makeSchema(self, name='schema'):
|
||||
|
||||
import colander
|
||||
|
||||
# an unlikely usage, but goos to test passing
|
||||
# parameters to instantiation works
|
||||
@colander.instantiate(name=name)
|
||||
class schema(colander.MappingSchema):
|
||||
int = colander.SchemaNode(colander.Int(),
|
||||
validator=colander.Range(0, 10))
|
||||
ob = colander.SchemaNode(colander.GlobalObject(package=colander))
|
||||
@colander.instantiate()
|
||||
class seq(colander.SequenceSchema):
|
||||
|
||||
@colander.instantiate()
|
||||
class tup(colander.TupleSchema):
|
||||
tupint = colander.SchemaNode(colander.Int())
|
||||
tupstring = colander.SchemaNode(colander.String())
|
||||
|
||||
@colander.instantiate()
|
||||
class tup(colander.TupleSchema):
|
||||
tupint = colander.SchemaNode(colander.Int())
|
||||
tupstring = colander.SchemaNode(colander.String())
|
||||
|
||||
@colander.instantiate()
|
||||
class seq2(colander.SequenceSchema):
|
||||
|
||||
@colander.instantiate()
|
||||
class mapping(colander.MappingSchema):
|
||||
key = colander.SchemaNode(colander.Int())
|
||||
key2 = colander.SchemaNode(colander.Int())
|
||||
|
||||
return schema
|
||||
|
||||
class Test_null(unittest.TestCase):
|
||||
def test___nonzero__(self):
|
||||
from colander import null
|
||||
|
@@ -132,6 +132,8 @@ Schema-Related
|
||||
|
||||
.. autoclass:: deferred
|
||||
|
||||
.. autoclass:: instantiate
|
||||
|
||||
.. attribute:: null
|
||||
|
||||
Represents a null value in colander-related operations.
|
||||
|
@@ -982,6 +982,63 @@ indeed be present in the child list of the ``schema`` instance
|
||||
``title`` attribute will be ``Some Schema`` (``schema.title`` will return
|
||||
``Some Schema``).
|
||||
|
||||
Defining A Schema Declaratively
|
||||
-------------------------------
|
||||
|
||||
Previously, we defined the schema in such a way that the individual
|
||||
sequences and mappings within the schema could be re-used in different
|
||||
schemas. If all nodes within a schema are only likely to be used in that
|
||||
schema, then the schema definition can be made more succinct using the
|
||||
:class:`~colander.instantiate` class decorator as shown below:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
import colander
|
||||
|
||||
class Person(colander.MappingSchema):
|
||||
name = colander.SchemaNode(colander.String())
|
||||
age = colander.SchemaNode(colander.Int(),
|
||||
validator=colander.Range(0, 200))
|
||||
|
||||
@colander.instantiate()
|
||||
class friends(colander.SequenceSchema):
|
||||
|
||||
@colander.instantiate()
|
||||
class friend(colander.TupleSchema):
|
||||
rank = colander.SchemaNode(colander.Int(),
|
||||
validator=colander.Range(0, 9999))
|
||||
name = colander.SchemaNode(colander.String())
|
||||
|
||||
@colander.instantiate()
|
||||
class phones(colander.SequenceSchema):
|
||||
|
||||
@colander.instantiate()
|
||||
class phone(colander.MappingSchema):
|
||||
location = colander.SchemaNode(colander.String(),
|
||||
validator=colander.OneOf(['home', 'work']))
|
||||
number = colander.SchemaNode(colander.String())
|
||||
|
||||
If you need to pass parameters when using this style of schema
|
||||
definition, such as a ``missing`` value to a :class:`SchemaNode`
|
||||
during instantiation, you can pass these as parameters to
|
||||
:class:`~colander.instantiate`.
|
||||
For example, if we wanted to limit the number of friends a person can
|
||||
have, and cater for people who have no friends, we could adjust the
|
||||
schema as shown below:
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
class Person(colander.MappingSchema):
|
||||
|
||||
@colander.instantiate(missing=(),
|
||||
validator=colander.Length(max=5))
|
||||
class friends(colander.SequenceSchema):
|
||||
|
||||
@colander.instantiate()
|
||||
class friend(colander.TupleSchema):
|
||||
name = colander.SchemaNode(colander.String())
|
||||
|
||||
Defining A Schema Imperatively
|
||||
------------------------------
|
||||
|
Reference in New Issue
Block a user