Posts by author vincent.millet

How to create your own validator

With web forms you always have to validate the data sent by the users of your application. Nagare offers you two simple validators: IntegerValidator and StringValidator. You can find them in the nagare.validator module. The point of this article is to show you how to easily make your own validators.

First, you need to know what an editor property is. A property is normally used in nagare to manage value in forms. When a form is submitted, the properties receive all the values. See the documention CallbacksAndForms#the-editor-property-objects.

Those properties also the offers the validate() method to set a validation function. The nagare documentation CallbacksAndForms#the-validator-module shows you a simple usage of a IntegerValidator

Let's start with a simple example of a StringValidator:

from nagare.validator import StringValidator
from nagare import editor
my_value = editor.Property()
my_value.validate(lambda v:  StringValidator(v).shorter_than(20).not_empty())

Once the property is created, we link a validator to it. Here we want the string to be shorter than 20 characters but not empty.

We can see the validate() method of a property takes a validating function in parameter. This function is called with the value to validate, raises the exception ValueError if the value is invalid or returns the value to be set (i.e a validation function can do conversions too). Then, we can try to put a value in our property:

>>> my_value(u'nagare.org')
u'nagare.org'
>>> my_value.input
u'nagare.org'
>>> my_value.value
u'nagare.org'
>>> my_value.error
>>> my_value(u'')
u''
>>> my_value.input
u''
>>> my_value.value
u'nagare.org'
>>> my_value.error
u'Can't be empty'

Now we know how to use a simple validator, let us see how to make a custom validator. To explain this part we choose to make an EmailValidator:

class EmailValidator(StringValidator):
    def __init__(self, value, **kw):
        super(EmailValidator, self).__init__(value, **kw)
        self.match('[0-9A-Za-z_\.-]+@[0-9A-Za-z_.-]+\.[A-Za-z]{2,4}', msg=u'Invalid email')

The first thing to do is to create a class which inherits from an existing validator: here we choose to inherit from StringValidator. Then, we can add the functionnality we want to offer or overload an existing function. For our example, we overload the __init__() method and we use the match() method inherited from the StringValidator class to check the email on a regular expression. The msg parameter is the message passed if a ValueError exception is raised.

>>> my_value.validate(EmailValidator)
>>> my_value(u'contact@nagare.org')
u'contact@nagare.org'
>>> my_value.input
u'contact@nagare.org'
>>> my_value.value
u'contact@nagare.org'
>>> my_value.error
>>> my_value(u'contact@')
u'contact@'
>>> my_value.input
u'contact@'
>>> my_value.value
u'contact@nagare.org'
>>> my_value.error
u'Invalid email'

For this example we use the match() method but you can also use all the already existing methods. For that, see directly the code of IntegerValidator and Stringvalidator in the nagare.validator module core/nagare/validator.py.

SQLAlchemy's MapperExtension

Problem

You use SQLAlchemy and you want to compute some models' attributes just before they are flushed to the database by SQLAlchemy (an SQL trigger, but on Python's side).

Solution

SqlAlchemy provides the MapperExtension interface with several methods to be implemented (eg.: before_insert, before_update, after_insert, after_update). You can see a full list of possible hooks in SqlAlchemy's documentation.

Let's see how to always capitalize the name of our users:

class User(object):
    def __init__(self, name):
        self.name = name
from sqlalchemy import Table, Column, Integer, Text
users_table = Table('users',
                     Column('id', Integer, primary_key=True),
                     Column('name', Text))

We want to modify the name before every SQL insert and every SQL update statement. The corresponding MapperExtension could be:

from sqlalchemy.orm import MapperExtension
class CapitalizeNameMapperExtension(MapperExtension):
    def _capitalize_name(self, instance):
        if instance.name is not None:
            instance.name = instance.name.capitalize()
    def before_insert(self, mapper, connection, instance):
        self._capitalize_name(instance)
    def before_update(self, mapper, connection, instance):
        self._capitalize_name(instance)

We can now map the model and the table using this extension:

from sqlalchemy.orm import mapper
user_mapper = mapper(User, users_table,
                     extension=CapitalizeNameMapperExtension())

So now, let's see it in action:

>>> user1 = User("mynameis")
>>> print user1.name
mynameis
>>> from nagare import database
>>> database.session.add(user1)
>>> print user1.name
mynameis
>>> database.session.flush()
>>> print user1.name
Mynameis

Note: this works on column-based attributes. At the call of the MapperExtension's functions, the mapper has already made the graph of objects to be updated in the database. This implies that if you make any change on collection attribute, it will not be stored in the database before the next flush call.