Extending Colander¶
You can extend Colander by defining a new type or by defining a new validator.
Defining a New Type¶
A type is a class that inherits from colander.SchemaType
and implements
these methods:
serialize
: converts a Python data structure (appstruct) into a serialization (cstruct).deserialize
: converts a serialized value (cstruct) into a Python data structure (appstruct).If it contains child nodes, it must also implement
cstruct_children
,flatten
,unflatten
,set_value
andget_value
methods. It may inherit fromMapping
,Tuple
,Set
,List
orSequence
to obtain these methods, but only if the expected behavior is the same.
Note
See also: colander.interfaces.Type
.
Note
The cstruct_children
method became required in Colander 0.9.9.
An Example¶
Here's a type which implements boolean serialization and deserialization. It
serializes a boolean to the string "true"
or "false"
or the special
colander.null
sentinel; it then deserializes a string (presumably
"true"
or "false"
, but allows some wiggle room for "t"
, "on"
,
"yes"
, "y"
, and "1"
) to a boolean value.
1from colander import SchemaType, Invalid, null
2
3class Boolean(SchemaType):
4 def serialize(self, node, appstruct):
5 if appstruct is null:
6 return null
7 if not isinstance(appstruct, bool):
8 raise Invalid(node, '%r is not a boolean' % appstruct)
9 return appstruct and 'true' or 'false'
10
11 def deserialize(self, node, cstruct):
12 if cstruct is null:
13 return null
14 if not isinstance(cstruct, basestring):
15 raise Invalid(node, '%r is not a string' % cstruct)
16 value = cstruct.lower()
17 if value in ('true', 'yes', 'y', 'on', 't', '1'):
18 return True
19 return False
Here's how you would use the resulting class as part of a schema:
1import colander
2
3class Schema(colander.MappingSchema):
4 interested = colander.SchemaNode(Boolean())
The above schema has a member named interested
which will now be
serialized and deserialized as a boolean, according to the logic defined in
the Boolean
type class.
Method Specifications¶
serialize
¶
Arguments:
node
: theSchemaNode
associated with this typeappstruct
: the appstruct value that needs to be serialized
If appstruct
is invalid, it should raise colander.Invalid
,
passing node
as the first constructor argument.
It must deal specially with the value colander.null
.
It must be able to make sense of any value generated by deserialize
.
deserialize
¶
Arguments:
node
: theSchemaNode
associated with this typecstruct
: the cstruct value that needs to be deserialized
If cstruct
is invalid, it should raise colander.Invalid
,
passing node
as the first constructor argument.
It must deal specially with the value colander.null
.
It must be able to make sense of any value generated by serialize
.
cstruct_children
¶
Arguments:
node
: theSchemaNode
associated with this typecstruct
: the cstruct that the caller wants to obtain child values for
You only need to define this method for complex types that have child nodes, such as mappings and sequences.
cstruct_children
should return a value based on cstruct
for
each child node in node
(or an empty list if node
has no children). If
cstruct
does not contain a value for a particular child, that child should
be replaced with the colander.null
value in the returned list.
cstruct_children
should never raise an exception, even if it is passed a
nonsensical cstruct
argument. In that case, it should return a sequence of
as many colander.null
values as there are child nodes.
Constructor (__init__
)¶
SchemaType does not define a constructor, and user code (not Colander) instantiates type objects, so custom types may define this method and use it for their own purposes.
Null Values¶
Both the serialize
and deserialize
methods must be able to
receive colander.null
values and handle them intelligently. This
will happen whenever the data structure being serialized or deserialized
does not provide a value for this node. In many cases, serialize
or
deserialize
should just return colander.null
when passed
colander.null
.
A type might also choose to return colander.null
if the value it
receives is logically (but not literally) null. For example,
colander.String
type converts the empty string to colander.null
within its deserialize
method.
1 def deserialize(self, node, cstruct):
2 if not cstruct:
3 return null
Defining a New Validator¶
A validator is a callable which accepts two positional arguments:
node
and value
. It returns None
if the value is valid.
It raises a colander.Invalid
exception if the value is not
valid. Here's a validator that checks if the value is a valid credit
card number.
1def luhnok(node, value):
2 """ checks to make sure that the value passes a luhn mod-10 checksum """
3 sum = 0
4 num_digits = len(value)
5 oddeven = num_digits & 1
6
7 for count in range(0, num_digits):
8 digit = int(value[count])
9
10 if not (( count & 1 ) ^ oddeven ):
11 digit = digit * 2
12 if digit > 9:
13 digit = digit - 9
14
15 sum = sum + digit
16
17 if not (sum % 10) == 0:
18 raise Invalid(node,
19 '%r is not a valid credit card number' % value)
Here's how the resulting luhnok
validator might be used in a
schema:
1import colander
2
3class Schema(colander.MappingSchema):
4 cc_number = colander.SchemaNode(colander.String(), validator=lunhnok)
Note that the validator doesn't need to check if the value
is a
string: this has already been done as the result of the type of the
cc_number
schema node being colander.String
. Validators
are always passed the deserialized value when they are invoked.
The node
value passed to the validator is a schema node object; it
must in turn be passed to the colander.Invalid
exception
constructor if one needs to be raised.
For a more formal definition of a the interface of a validator, see
colander.interfaces.Validator
.