3

Say I have a simple schema:

class MySchema(colander.MappingSchema):
    thing = colander.SchemaNode(colander.Int())

With the schema above, when trying to deserialize {'thing': None} I get the error:

Invalid: {'thing': u'Required'}

It looks like colander treats fields with a None value the same way as missing fields. How can I get around that and enforce that thing is always provided, but allow it to be None?

Jules Olléon
  • 6,733
  • 6
  • 37
  • 47

3 Answers3

4

Please consider this solution.

import colander


class NoneAcceptantNode(colander.SchemaNode):
    """Accepts None values for schema nodes.
    """

    def deserialize(self, value):
        if value is not None:
            return super(NoneAcceptantNode, self).deserialize(value)


class Person(colander.MappingSchema):
    interest = NoneAcceptantNode(colander.String())


# Passes
print Person().deserialize({'interest': None})

# Passes
print Person().deserialize({'interest': 'kabbalah'})

# Raises an exception
print Person().deserialize({})
negus
  • 801
  • 6
  • 7
2

A None value will work for deserialization, however you need to supply a 'missing' argument in your schema:

class MySchema(colander.MappingSchema):
    thing = colander.SchemaNode(colander.Int(), missing=None)

http://docs.pylonsproject.org/projects/colander/en/latest/null.html#deserializing-the-null-value

tim-phillips
  • 997
  • 1
  • 11
  • 22
  • This doesn't help: as highlighted in [this table](http://docs.pylonsproject.org/projects/colander/en/latest/null.html#deserialization-combinations) on the page you linked, using `colander.null` will still raise an `Invalid` exception. `colander.null` and a missing value are pretty much the same thing for colander. And my problem is that `None` seems to also be treated the same although I can't find any documentation on this behavior. – Jules Olléon Sep 13 '13 at 02:52
  • Ah ok, it looks like you actually need to set a default missing value in your schema. I've updated my answer to include this. – tim-phillips Sep 15 '13 at 18:09
  • Still doesn't do what I want... When using `missing=None`, even if `thing` is not present it will be added and set to `None`. If `thing` doesn't appear in the data I want this to raise, not automatically add it. – Jules Olléon Sep 16 '13 at 21:40
  • You want an error to be raised? Isn't that already happening? – tim-phillips Sep 24 '13 at 20:29
  • I want an error to be raised when `thing` is not present. I don't want an error to be raised if `thing` is present (even if it's `None`). – Jules Olléon Sep 25 '13 at 22:45
0

Here's what I'm using. I have the empty string mapped to an explicit null value. If the required flag is true, then it raises an invalid error.

from colander import SchemaNode as SchemaNodeNoNull

class _SchemaNode(SchemaNodeNoNull):

    nullable = True

    def __init__(self, *args, **kwargs):
        # if this node is required but nullable is not set, then nullable is
        # implicitly False
        if kwargs.get('missing') == required and kwargs.get('nullable') is None:
            kwargs['nullable'] = False
        super(_SchemaNode, self).__init__(*args, **kwargs)

    def deserialize(self, cstruct=null):
        if cstruct == '':
            if not self.nullable:
                raise Invalid(self, _('Cannot be null'))
            if self.validator:
                self.validator(self, cstruct)
            return None  # empty string means explicit NULL value
        ret = super(_SchemaNode, self).deserialize(cstruct)
        return ret

Also, when dealing with querystring parameters, foo=,bar= will become:

{
   "foo": "",
   "bar": ""
}

A literal null value is only possible with JSON payloads

JamesHutchison
  • 885
  • 2
  • 8
  • 17