In todays episode we will use Colander with validators for nested elements :) Checkout my experience with them.

This episode is a series about colander

S0-E8/E30 :)

Colander Nested

Let's say you have a nested list elements that also have atributes like this :

{
    "users": [
        {
            "id": "1",
            "name": "Anselmos",
        },
        {
            "id": "1",
            "name": "Somlesna",
        }
    ],
    "list_name": "name_of_users"
}

Code that I would typically create would look like this:

import json
dict_1 = {
    "users": [
        {
            "id": "1",
            "name": "Anselmos",
        },
        {
            "id": "1",
            "name": "Somlesna",
        }
    ],
    "list_name": "name_of_users"
}
api_data = json.dumps(dict_1)
print api_data
json_loaded = json.loads(api_data)
dicted = dict(json_loaded)
print dicted

class JsonDeserialize:
    def __init__(self, **entry):
        self.__dict__.update(entry)

classed = JsonDeserialize(**dicted)
print classed.users
print classed.users[0]

What is the problem with it ? Well you still have to manually create a validator for each user list-element.

Colander can fix that with:

import json
import colander
dict_1 = {
    "users": [
        {
            "id": "1",
            "name": "Anselmos",
        },
        {
            "id": "1",
            "name": "Somlesna",
        }
    ],
    "list_name": "name_of_users"
}
api_data = json.dumps(dict_1)
print api_data
json_loaded = json.loads(api_data)
dicted = dict(json_loaded)
print dicted

class User(colander.MappingSchema):
    id = colander.SchemaNode(colander.Int())
    name = colander.SchemaNode(colander.String())

class Users(colander.SequenceSchema):
    user = User()

class Data(colander.MappingSchema):
    users = Users()
    list_name = colander.SchemaNode(colander.String())

serialized= Data().serialize(dicted)
import pprint
print pprint.pformat(serialized)
deserialized = Data().deserialize(dicted)
print pprint.pformat(deserialized)

Now I now this looks pretty simple. But let's make another example that will use validator - which you could not create with simple json and dict "hack" :

import json
import colander
dict_1 = {
    "users": [
        {
            "id": "1",
            "name": "Anselmos",
        },
        {
            "id": "1",
            "name": "Somlesna",
        }
    ],
    "list_name": "name_of_users"
}
api_data = json.dumps(dict_1)
print api_data
json_loaded = json.loads(api_data)
dicted = dict(json_loaded)
print dicted

class User(colander.MappingSchema):
    id = colander.SchemaNode(colander.Int(), validator=colander.NoneOf([1]))
    name = colander.SchemaNode(colander.String())

class Users(colander.SequenceSchema):
    user = User()

class Data(colander.MappingSchema):
    users = Users()
    list_name = colander.SchemaNode(colander.String())

serialized= Data().serialize(dicted)
import pprint
print pprint.pformat(serialized)
deserialized = Data().deserialize(dicted)
print pprint.pformat(deserialized)

Oh ... now it will fail with :

local/lib/python2.7/site-packages/colander/__init__.py", line 704, in _impl
    raise error
colander.Invalid: {'users.0.id': u'"1" must not be one of 1',
 'users.1.id': u'"1" must not be one of 1'}

Because we added to our schema validator like that:

    id = colander.SchemaNode(colander.Int(), validator=colander.NoneOf([1]))

Ofcourse the obvious fix for that will be to change id's for user's to different than 1.

Colander build-in validators

There are 13 build-in colander validators that we can make great use.

I encourage you to try them :)

1. colander.All(*validators)

2. colander.Any(*validators)

3. colander.Range(min=None, max=None, min_err='${val} is less than minimum value ${min}', max_err='${val} is greater than maximum value ${max}')

4. colander.Length(min=None, max=None, min_err='Shorter than minimum length ${min}', max_err='Longer than maximum length ${max}')

5. colander.OneOf(choices)

6. colander.NoneOf(choices, msg_err='"${val}" must not be one of ${choices}')

7. colander.ContainsOnly(choices)

8. colander.Function(function, msg=None, message=None)

9. colander.Regex(regex, msg=None, flags=0)

10. colander.Email(msg=None)

11. colander.luhnok(node, value)

12. colander.url

13. colander.uuid

Acknowledgements

Thanks!

That's it :) Comment, share or don't :)

If you have any suggestions what I should blog about in the next articles - please give me a hint :)

BTW - today is the last day when I eat junk food - why ? Because I'm starting another challenge for not eating junk food for the time of fasting - so at least 40 days :) - So Keep finger crossed for me :)

See you tomorrow! Cheers!



Comments

comments powered by Disqus