In PythonOnWheels you define your models using a cerberus schema. This doesn't only give you the benefit of using the same model definition schema for NoSQL and all SQL Databases but it also means that you have validation on board for every model and you can easily switch from SQL to NoSQL
That's pretty handy. We will focus on model validation in this hands-on documentation. If you don't know how to generate models just read the short models intro Or go to the in-depth model documentation.
For this tutorial I generated a TinyDB (NoSQL DB) model like this:
python generate_model.py -n todo -t tinydbAnd this is how the generated model schema looks like:
#
# TinyDB Model: Todo
#
from testapp.models.tinydb.tinymodel import TinyModel
class Todo(TinyModel):
#
# Use the cerberus schema style
# which offer you immediate validation with cerberus
# http://docs.python-cerberus.org/en/stable/validation-rules.html
# types: http://docs.python-cerberus.org/en/stable/validation-rules.html#type
#
schema = {
'title' : { 'type' : 'string', 'maxlength' : 35 },
'text' : { 'type' : 'string' },
'tags' : { 'type' : 'list', "default" : [] },
"votes" : { "type" : "integer", "default" : 0 }
}
A PythonOnWheels SQL model would use the exact same definition syntax. Which is a cerberus schema for all PythonOnWheels models. Cerberus is a lightweight and extensible validation library for python. It's purpose is to define datatypes and constraints by defining a schema. It also offers validation for the actual models against the constraints in the schema.
So with our schema we can:
PythonOnWheels generates the actual model implementations from the cerberus schemas for you. So when you define a model like the above. PoW generates a mongoDB, tinyDB, sqlalchemy schema in the background that is actually used to work with the database of your choice.
Let's look again at our model's schema definition, because the validation uses our definition to check if an actual model instance is correct or not.
schema = {
'title' : { 'type' : 'string', 'maxlength' : 35 },
'text' : { 'type' : 'string' },
"status": { "type" : "string", "default" : "new", "allowed" : ["new", "wip", "done", "hold"]},
"votes": { "type" : "integer", "default" : 0 }
}
To ease this just copy and replace the whole schema.
But we just rename tags to status and add the "allowed" : ["wip", "done", "hold"] definition.
You can see that we defined four attributes title, text, status and votes and we also defined their types. Which are all strings, only the points attribute is of type integer. This is the first part that will be checked. Are the types of a model instance correct ?
For some of the attributes, in this case title and status we also defined constraints. Constraints just narrow the allowed values for an attribute type. For the title attribute the type has to be a string with the constraint that it may not be longer than 35 characters. The status attribute has to be of type string as well, but it also only validates if the string is one of the allowed: "new", "wip", "done" or "hold". Since we use cerberus you can rely on a lot of more possible constraint definitions. Just read the full validation rules docs.
Fire up your python interpreter and import your model. If you don't know how to generate a PythonOnWheels app read the 2 Minute intro. See above how to generate a model.
>>> from testapp.models.tinydb.todo import Todo>>> t=Todo()"status": { "type" : "string", "default" : "new", "allowed" : ["new", "wip", "done", "hold"]},
As you know, this defines status to be of type string AND only accepts the allowed values which are in this case: new, wip, done and hold. Any other value will be validated as False.
>>> t.title="test"
>>> t.status="yihaa"
>>> r=t.validate()>>> r
>>> (False, <cerberus.validator.Validator object at 0x10c7b2160>) The most common case is to check the current errors attribute.
>>> r[1].errors
{'status': ['unallowed value yihaa']}
>>> t.status="done">>> r=t.validate()
>>> r
(True, None)
I hope you agree that using cerberus schemas makes it really easy not only to define database models in an elegant way but also gives us a lot of benefit with validation as well. The schema syntax is very straight forward using the datatypes available in python as well and easy to remember constraints like "allowed", "maxlength", "anyof" and so on.
Hope you enjoy PythonOnWheels. If you have any questions or remarks or errors you can open an issue on github or tweet to @pythononwheels.