To solve the problem with the supplied validation decorator, I came up
with a new validator the abstracts the different part of form handling
in the controller action. You use the new decorator on top of a action
method, in a similar way to the old decorator, but you pass a form
"handler" class in addition to the form schema class:

@validate(MySchema(), MyHandler())
def register(self):
    c.info_for_template = f(request.params)
    return render('my_form.mako')

Note that in the action method itself you only need to display the
form's page, and not do any pre- or post-processing. The decorator
will take care (using the handler class) of performing any checks,
validate the form (on POST reuests),
perform post processing if validation succeeded, or call your action
method to render the form and populate it with initial values or
values from the last POST request.

The schema class that is passed as the first argument to the decorator
is the same as for the old validate decorator - it's a class derived
from formencode.Schema that you define for your form, for example:

class MySchema(formencode.Schema):
    email = formencode.validators.Email(not_empty=True)
    name = formencode.validators.MinLength(2)

The real magic comes from the "form handler" class which you define as
a derived class of the new "FormHandler" class, for example:

class MyHandler(FormHandler):
    def check(self):
        if session.has_key('user'):
            redirect_to(action='error')   # can't register if already
logged-in
        return None                       # everything OK

    def process(self, result):
        user=model.User(name=result['name'], email=result['email'])
        user.save_to_db()
        session['flash']='Thank you for registering'
        session.save()
        redirect_to('homepage')


The most important method to override is "process" which is called
after the form has been validated successfully. In this method you
will normally perform the actions which the form is designed for, such
as creating a new record in the database. The form's values are passed
in the argument "result".

You don't really need to define any of the other methods, but they are
useful in some situations.

The "check" method is called by the decorator in the beginning of
processing every request (the first GET request, and all subsequent
POST requests). Here you can check permissions and other pre-
conditions, and return "None" if everything is OK. If something is
wrong and you don't want the form to be displayed, then you can
redirect or return a different page (e.g. using mako_rander). Note
that this method is mostly for convenience, because you can achieve
the same thing by defining another method in your controller that
perfoms the same checks and then calls the method with the "validate"
decorator.

Another optional method is "defaults" which is called only once before
rendering the form on the first GET request, and is supposed to return
the initial values of the form's fields in a dict-like object. The
default "defaults" are taken from the request params.

The last method is "validation_ctx" and it's called by the "validate"
decorator just before performing form validation and is supposed to
return a "state" object that is passed to the form's (user-defined)
validators which were declared in the schema. Note that you can always
use the "c" global to pass information to the validators, but in any
case you can use this method to populate your chosen context.

Using this decorator has greatly simplified my form handling code, so
if there is interest I can publish the source code for this decorator/
handler architecture.


On Jan 3, 4:17 pm, Tycon <[email protected]> wrote:
> I was trying to use this decorators like this:
>
> @validate(schema=MySchema(), form='action')
> def action(self):
>      if request.method=='GET':
>             return render('/form.mako')
>      else:
>            return _process_form(self.form_result)
>
> But there are a few problems with this approach:
>
> 1. The first time the controller action is called, it renders the
> template without removing the special error placement tags.
> 2. There is no way to set default values for the form other than
> coding them into the template. It would be better to be able to set
> default values that would be filled only the first time the form is
> rendered.
> 3. There is no way to pass a dynamic state object to the decorator,
> that would be passed to the form validators. This is because the
> decorator is called at compile time, so the you can only pass to it
> static objects as the "state".
> 4. You actually have to specify the name of the action method that
> renders the form as an argument to the decorator, and this method
> can't be private (so even if it's not the original action method for
> this request, it is callable as an action method directly by itself).
> 5. The action method will be run every time the form has to be
> rendered, e.g. after every failed validation,
> so you can't use it to perform initial one-time checks.
>
> All the above problems happen because using the decorator approach to
> do too many things using a single method. Trying to work around the
> limitations by breaking up the action method into several different
> functions also doesn't work well when using this decorator. So it
> seems like it's better to do the validation the "long" way, or at
> least redesign this decorator to be a class with different methods
> that can be overridden.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/pylons-discuss?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to