Files
pecan/docs/source/validation_n_errors.rst
2011-03-06 14:31:18 -05:00

5.7 KiB

Validation and Error Handling ============ Pecan provides a variety of tools to help you handle common form validation and error handling activities, like:

  • Validating the presence of submitted form contents with a schema.
  • Transforming strings from form submissions into useful Python objects.
  • Simplifying the process of re-displaying form values and associated error messages inline.

Rather than re-inventing the wheel, Pecan uses FormEncode for schemas and form validation.

Writing and Applying Schemas

Here's a simple example of a schema and how to apply it to a controller method using Pecan's expose decorator:

from pecan import expose
from formencode import Schema, validators as v

class SimpleSchema(Schema):    
    username = v.String(not_empty=True)
    password = v.String(not_empty=True)

class LoginController(object):

    @expose(schema=SimpleSchema)
    def login(self, **kw):
        if authenticate(
          kw['username'],
          kw['password']
        ):
          set_cookie()
        return dict()

Validating JSON Content

In addition to simple form arguments, Pecan also makes it easy to validate JSON request bodies. Often, especially in AJAX requests, the request content is encoded as JSON in the request body. Pecan's validation can handle the decoding for you and apply schema validation to the decoded data structure:

from pecan import expose
from formencode import Schema, validators as v
from myproject.lib import authenticate

class JSONSchema(Schema):
    """
    This schema would decode a JSON request body
    that looked like:

    {
      'username' : 'pecan',
      'password' : 'dotpy
    }

    """
    username = v.String(not_empty=True)
    password = v.String(not_empty=True)

class LoginController(object):

    @expose(json_schema=JSONSchema)
    def login(self, **kw):
        authenticate(
          kw['username'],
          kw['password']
        )
        return dict()

Handling Schema Failures

When schema validation fails, the validation errors from FormEncode are applied to pecan.request.validation_errors. This list can be browsed in your controller methods to react to errors appropriately:

from pecan import expose, request
from myproject.schemas import SimpleSchema

class LoginController(object):

    @expose(schema=SimpleSchema)
    def login(self, **kw):
        if request.validation_errors:
            pass # Don't Panic!
        return dict()

Error Handlers and Template Filling ------------------------------When schema validation fails, Pecan allows you to redirect to another controller internally for error handling via the error_handler keyword argument to @expose(). This is especially useful when used in combination with generic controller methods:

from pecan import request, expose
from formencode import Schema, validators as v

class ProfileSchema(Schema):    
    name = v.String(not_empty=True)
    email = v.String(not_empty=True)

class ProfileController(object):

    @expose(generic=True)
    def index(self):
        pass

    @index.when(method="GET", template='profile.html')
    def index_get(self):
        """
        This method will be called to render the original template.
        It will also be used for generating a form pre-filled with values
        when schema failures occur.
        """
        return dict()

    @index.when(method="POST", schema=ProfileSchema(), error_handler=lambda: request.path)
    def index_post(self, **kw):
        """
        This method will do something with POST arguments.
        If the schema validation fails, an internal redirect will
        cause the `profile.html` template to be rendered via the
        ``index_get`` method.
        """

        name = kw.get('name')
        email = ke.get('email')

        redirect('/profile')

In this example, when form validation errors occur (for example, the email provided is invalid), Pecan will handle pre-filling the form values in profile.html for you. Additionally, inline errors will be appended to the template using FormEncode's htmlfill.

Bypassing htmlfill

Sometimes you want certain fields in your templates to be ignored (i.e., not pre-filled) by htmlfill. A perfect use case for this is password and hidden input fields. The default Pecan template namespace includes a built-in function, static, which allows you to enforce a static value for form fields, preventing htmlfill from filling in submitted form variables:

<form method="POST">
  <dl>
    <dt>Username:</dt>
      <dd><input type="text" name="username" /></dd>
    <dt>Password:</dt>        
      <dd><input type="password" name="password" value="${static('password', '')}" /></dd>
    <input type="hidden" name="ticket" value="${static('ticket', 'RANDOM_PER_REQUEST_VALUE')}" />
  </dl>
  <button>Login</button>
</form>

Working with variabledecode ------------------------------Pecan also lets you take advantage of FormEncode's variabledecode for transforming flat HTML form submissions into nested structures:

from pecan import expose
from myproject import SimpleSchema

class ProfileController(object):

    @expose(schema=SimpleSchema(), variable_decode=True)
    def index(self):
        return dict()