05
qr-code

FormEncode: (2) Compound Validation

Suppose we have a form in which two inputs need to be the same, i.e.: email matching or password matching.

import formencode
from formencode import validators as v


class Registration(formencode.Schema):
    email_input = v.Email(strip=True, resolve_domain=True)
    email_input_confirm = v.Email(strip=True, resolve_domain=True)
    chained_validators = [v.FieldsMatch("email_input",
                                        "email_input_confirm")]

chained_validators runs after all other validators inside of Registration is done. v.FieldsMatch will verify that the two given fields match with each other.

If we have a input field which expect a user input a integer in between 0 and 99. When we get the input in the backend, it's always a string. Before we need to validate using OneOf, we need to convert the input from string to int via Int.

import formencode
from formencode import compound as c
from formencode import validators as v


class Calculator(formencode.Schema):
    # All validates backwards
    numerator = c.All(v.OneOf(range(100)), v.Int())
    # Pipe validates forwards
    # numerator = c.Pipe(v.Int(), v.OneOf(range(100)))

Now suppose we have a form with a group of employees. Each employee has his/her own first name, last name, email, and phone number. In the frontend, a user can add a new employee or delete an existing employee. In another word, we don't know how many employees ahead we are going to validate. Thus, we can not use Brute-force validation like we did before. Formencode provides a function called formencode.ForEach which helps us solve similar problems.

Here is how our form looks like:

<form>
    <table>
        <tr>
            <td><input type="text" name="employee-0.first_name"></td>
            <td><input type="text" name="employee-0.last_name"></td>
            <td><input type="text" name="employee-0.email"></td>
            <td><input type="text" name="employee-0.phone"></td>
        </tr>
        <tr>
            <td><input type="text" name="employee-1.first_name"></td>
            <td><input type="text" name="employee-1.last_name"></td>
            <td><input type="text" name="employee-1.email"></td>
            <td><input type="text" name="employee-1.phone"></td>
        </tr>
        <tr>
            <td><input type="text" name="employee-2.first_name"></td>
            <td><input type="text" name="employee-2.last_name"></td>
            <td><input type="text" name="employee-2.email"></td>
            <td><input type="text" name="employee-2.phone"></td>
        </tr>
        ...
    </table>
</form>

The validation class definition will be

import formencode
from formencode import validators as v
from formencode import national as n


class Employee(formencode.Schema):
    first_name = v.String(strip=True)
    last_name = v.String(strip=True)
    email = v.String(strip=True, resolve_domain=True)
    phone = n.USPhoneNumber()


class Employees(formencode.Schema):
    employee = formencode.ForEach(Employee())
Posted on .
blog comments powered by Disqus