Section 16.4. Widgets and Validation

16.4. Widgets and Validation

We briefly saw how validation works in Chapter 5, but there's a lot more power and flexibility in the validation library TurboGears uses than we've had a chance to cover. Not only that, widgets have some special built-in features that help make form validation even easier and more painless.

Widget-based forms automatically handle validation errors, display error messages, and translate data from Python objects into the strings used in the form, and back again when the form is processed. We've already seen most of this in action in Chapter 5, so you probably have a general idea of what happens. But in the next few pages we're going to peel the covers back a bit and take a look at how all of this works and how you can customize things to get exactly the behavior you want from your forms.

16.4.1. How FormEncode Validators Integrate into Form Widgets

As we mentioned, nearly every web application is going to include forms and nearly every set of forms has required fields or other input validation associated with them. Not only that, nearly every form gets some data from the user that ought to be converted from a string (all form results come from the browser as strings) into a proper Python object. You want age to be an int, dates to be datetime objects, etc.

Coding all of this to work by hand isn't rocket science, but it's tedious and repetitive. So, TurboGears needed a way to get rid of all the repetitive stuff for you. Because the TurboGears philosophy is to use the best existing libraries wherever possible, it provides easy access to Ian Bicking's FormEncode package.

From day one TurboGears provided convenient access to Form Encode Validators via controller decorators (see Chapter 17, "CherryPy and TurboGears Decorators," for details). Widgets take this one step further and automate error display.

FormEncode is designed to convert and validate incoming data and produce validation error messages when necessary. The TurboGears validation mechanism ties directly into FormEncode to preprocess incoming form data so your controller objects get form data in the proper Python type.

If there are errors, the error_handler appends the validation error messages to the cherrypy.request object (more about all of this in Chapter 17, and sends the user to another controller method which can handle the errors.

Form widgets take this one step further, integrating FormEncode into the widget declaration/display process. This means that you can tell a form to act as its own error_handler, and it will take care of displaying validation errors for you.

We've already seen that widgets display input_value and validation error messages when their contents fail to validate, but let's take a quick look at how this all works.

The built-in form widgets know how to display their own errors. We didn't mention it earlier for the sake of keeping things simple, but TurboGears form widgets also check the cherrypy.request object to see if there are validation errors present and if errors are present, displays them next to the proper form element.

At the same time, form widgets know how to pull the input_value for each form element off the request object. The input_value contains whatever value was submitted by the user when she filled out the form. Because TableForm knows how to check input_value and validation_errors and display them properly, all you have to do is set validators on the field elements, and set up the save method's error_handler to point to a controller method that uses the same form widget to display validation errors.

16.4.2. More Validators

Sometimes you need more than an Int() validator. Fortunately, FormEncode provides a number of built-in validators that you can use. Not only that, it provides you with a simple mechanism for creating your own validators.

More often than not one of the built-in validators can give you what you needthe regex validator is particularly flexible. If you want to check that the user entered a valid number, a valid date, a valid URL, or a valid e-mail address, there are built-in validators to help you.

The most-up-to date list of built-in validators is available at Here's a list of the most commonly used validators:

  • Constant If you want to ignore the user's input, and always use a constant instead, this is the validator for you

  • CreditCardValidator Accepts only credit card numbers in a valid format (note it does not actually check that the card number is real)

  • DateConverter Converts a date mm/yy, dd/mm/yy, dd-mm-yy into a datetime object. DateConverter always assumes the second value is the month

  • DateValidator Checks that the user-entered date is within specified range (useful, for example, if you want to check that a user's birth date is over 18 years ago)

  • DictConverter Takes a list of key-value pairs, and matches user input against the keys to return a value

  • Email Checks the basic form of an email address

  • Empty Validates that the user didn't enter any value. (Fill in this textfield now or forever hold your peace…)

  • IndexListConverter Converts an index (which may be a string like "2") to the value in the given list

  • Int Converts a value to an integer

  • MaxLength Confirms that the string is shorter than the int you pass in

  • MinLength Confirms that the string is longer than the int you pass in

  • NotEmpty Confirms that the value is not empty (empty string, empty list, etc.)

  • Number Converts a value to a float or integer

  • OneOf Tests that the value is one of the members of a given list

  • PhoneNumber Validates, and converts to ###-###-####. It can even handle extensions (as ext.##…)

  • PlainText Confirms that the field contains only letters, numbers, underscore, and the hyphenno special characters allowed. (This can be useful for security purposes, which we'll discuss in a bit more detail in Chapter 22, "TurboGears Identity and Security")

  • PostalCode Verifies that the number is a valid US Zip Code

  • Regex Checks the user input against a regular expression you provide

  • StateProvince Verifies that the user entered a valid two letter state or province code (US and Canada only)

  • String Converts data to string, but treats empty objects as the empty strings

  • TimeConverter Converts user entered times in the format: HH:MM:SSampm to a tuple in the format: (h, m, s)

  • URL Validates that the returned string is a structurally valid URL

Sometimes you want more than any one of these validators can give you. You want to assure that a form field is a valid URL, and it's at least 10 characters long. That's where compound validators come inFormEncode provides the any and all validators, which do pretty much what they say. If you write:

mywidget=widgets.TextField(name = "int_or_plan_text",                     validator=validators.any(validators.PlainText(),                                              validators.Int())

The mywidget widget defined above will accept input as valid if either the value entered by the user is plain text, or an integer.

If, on the other hand, you wanted to check that the returned URL string didn't have any special characters, you can write something like this:

mywidget=widgets.TextTield(name= "URL_and_more_than_10_characters"),                                   validators.all(validators.plaintext(),                                                  validators.MinLength(15))

As we mentioned earlier, the Regex validator can handle most of your string validation needs. But, since the purpose of validators is not just to specify rules about the contents of user values, it is also to convert user entered values into usable Python objects, it is often advantageous to create your own validators. Fortunately that's actually pretty simple to do.

Creating your own validator is a three-to-four step process. Validators need to know how to convert Python object strings, how to convert strings to the right kind of Python object, and what kind of messages to display when something goes wrong. To create a new validator class:

  1. Subclass FancyValidator.

  2. Create an error messages dictionary.

  3. Define a _to_python method that will convert the string returned by the browser into whatever Python object you want, and if necessary, create a validate_python method that will test the Python object in some way and raise an Invalid error when needed.

  4. If necessary, define a _from_python method that will convert the Python object you have into a string for the browser.

Don't let the fact that this is a four-step process fool you into thinking that it's complicated. Here's the code for a simple validator:

class isWikiWord(validators.FancyValidator):     wiki_regex = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")     messages = {'non_wiki': 'Your Page Name must be a WikiWord'}     def _to_python(self, value, state):         return value.strip()     def validate_python(self, value, state):         if not self.wiki_regex.match(value):         raise validators.Invalid(self.message("non_wiki"),                              value, state)

If you have more than one failure mode, you just define the additional failure messages in your messages dictionary, and add additional raise Invalid exceptions to your _to_python or validate_python methods.

The FancyValidator class gives your validator several options which can be used when creating a new instance.

  • if_empty If set, this value will be returned if the input evaluates to false (empty list, empty string, None, etc.), but not the 0 or False objects. This only applies to to_python.

  • not_empty If true, then if an empty value is given raise an error. If validate_python true this will also check that the Python value is not empty, too.

  • strip If true and the input is a string, strip it (occurs before empty tests).

  • if_invalid If set, then when this validator raises Invalid during to_python, this value is returned instead.

  • if_invalid_python If set, when the Python value (converted with from_python) is invalid, this value will be returned.

  • accept_python If true then .validate_python and .validate_other will not be called when from_python is called.

16.4.3. Basics of Schema Validation

Sometimes you can't validate a piece of user input in isolation. Password verification fields are a common example. You want the data in each of those two fields to match. This is where schema validation comes in. Schemas are pretty simple as long as you don't have deeply nested compound widgets to match up with your schema.

If you have two password fields by themselves in the change_password form, you could create a schema that looks like this:

class UserFields(widgets.WidgetsDeclaration):     passwd = widgets.PasswordField(validator=validators.NotEmpty())     passwd2 = widgets.PasswordField(validator=validators.UnicodeString()) class UserSchema(formencode.schema.Schema):     chained_validators = [validators.FieldsMatch('passwd', 'passwd2')] form = TableForm(fields=UserFields(), validator=UserSchema)

The UserSchema class uses chained_validators, which are validators run on the dictionary of form responses after all the other validators are done. Since the schema is just another validator, you can create nested validation schemas, to do chain validation on nested dictionaries. In general you'll find your life is easier if you don't create too many nested dictionaries in need of validation (after all in Python "flat is better than nested").

There's another built-in validator designed especially for schema validation:

  • FieldsMatch Checks two fields to verify that they are identical.

Rapid Web Applications with TurboGears(c) Using Python to Create Ajax-Powered Sites
Rapid Web Applications with TurboGears: Using Python to Create Ajax-Powered Sites
ISBN: 0132433885
EAN: 2147483647
Year: 2006
Pages: 202

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: