On Sep 5, 2006, at 12:41 PM, BJörn Lindqvist wrote:

>
> Hello.
>
> I'm trying to understand how TurboGears validation of form works.
> Specifically, I want to know what TurboGears does "behind the scenes."
> I have followed the tutorial on this page:
> http://trac.turbogears.org/turbogears/wiki/SimpleWidgetForm. The
> relevant code is this:
>
> class Root(RootController):
>     ....
>     @expose(template=".templates.add")
>     def add(self, tg_errors=None):
>         if tg_errors:
>             flash("There was a problem with the form!")
>         return dict(form=comment_form)
>
>     @expose()
>     @validate(form=comment_form)
>     @error_handler(add)
>     def save(self, name, email, comment, notify=False):
>         comments.add(name, email, comment)
>         if notify:
>             flash("Comment added! You will be notified.")
>         else:
>             flash("Comment added!")
>         raise redirect("index")
>
> What I'm wondering is how does the decorators validate and
> error_handler really work? Validate seem to magically change the
> arguments to save() to python objects instead of text strings.

Depends on your definition of "magic" and your understanding of  
decorators.... ;)
A decorator is basically a wrapper around a function [1]. What  
validate more or less does when it decorates your method is something  
like:

def validate(*args, **kw):
        params = make_all_args_kw_args(args, kw)
        try:
                params = validate_params_with_your_form(params)
        except ValidationFailed, exception:
                # Opps, bad input. Let's call the method which shall handle 
errors
                # (as registered by one or many "error_handler" decorators)
                return  dispatch_the_exception_using_error_handling 
(your_controller_method, params, exception)
        else:
                # validation passed and params contain good python objects. 
Let's  
put them back
                # into args and kw and call the decorated method
                args, kw = restore_original_args_and_kw(params)
                return your_controller_method(*args, **kw)

Disclaimer: *Highly* simplified, but you can get an idea.... The  
whole story lives here [2]



> Then
> the error_handler decorator I think makes a HTTPRedirect back to add()
> if validation fails. But how does it manage to display the error
> messages in the form?

Not exactly: error_handler only appends rules to the  
"dispatch_the_exception_using_error_handling" generic function (which  
is actually called "dispatch_error"). As you can see in the pseudo- 
code, It's dispatch_error who actually calls the function you  
registered using "error_handler" when validation fails.

This is highly simplified: "dispatch_error" can actually also catch  
uncaught exceptions raised inside your controller method and can be  
**very** flexible and **very** powerful. For example, you can define  
rules to catch any kind of exception inside any controller method to  
log it or give a custom error page. Or you can log specific Invalid  
exceptions to see where your users most often make mistakes in your  
forms. Or you can catch TurboPeakSecurity's (hey! it's been long  
since I don't self-promote myself!... ;)) SecurityDenial exceptions  
when some unauthorized action wants to take place [3], or... it's  
really up to your imagination once you get your brain around  
errorhandling and generic functions....

Honestly, I think errorhandling is one of TG's slickest feature and  
the form_widgets/errorhandling combo one of TG's strongest asset  
(Mark, you can quote me on this for the book. ;) )

> How does the form know that it should display
> the values that you wrote there previously when you are redirected
> back?

Again, it's not actually a HTTP redirect so you're in the same  
request the input came in. This allows us to use cherrypy.request to  
store all this information: The resulting validation exception is  
stored in "cherrypy.request.validation_exception", the unpacked  
exception is stored as a (possibly nested) dict in  
"cherrypy.request.validation_errors" and the input values in  
"cherrypy.request.input_values".

When the form is displayed it checks these request variables to see  
if it's being redisplayed [4], if it is, it just ignores the value  
your controller gave it, displays the values the user submitted and  
the errors along the fields.

> I would like to implement code similar to the example above, but
> without using a form widget. And preferably without using the validate
> and error_handler decorators. Maybe someone can show me an example of
> that?

Well, good luck ;) As Karl pointed out, all this magic took quite a  
lot of hard work and time to design, implement, test and debug by the  
TG community to free you from writing all the boiler-plate code. If  
you really want to do it without using widgets, errorhandling and  
decorators there's absolutely nothing from stopping you. You could do  
more or less the same stuff with something like:


def show_form(self, errors=None, input_value=None):
        # build your form, populate it, etc.... here
        return dict(....)

def process_form(self, **kw)
        try:
                kw = your_schema.validate(kw)
        except Invalid, e:
                errors = e.unpack_errors()
                return self.show_form(errors=errors, input_value=kw)
        # good python objects @ kw from now on... do something useful
        # with them

However, I'm sure you'll appreciate how simpler and slicker your  
controller methods will get when leaving all the boring stuff  to  
TG's "magic" internals when you get a hang of it (after implementing  
a couple of these boring show_form/process_form pairs... ;) )

HTH,
Alberto

[1] http://en.wikipedia.org/wiki/Decorator_pattern
[2] http://trac.turbogears.org/turbogears/browser/branches/1.0/ 
turbogears/controllers.py#L101
[3] http://trac.toscat.net/TurboPeakSecurity/file/trunk/tpsecurity/ 
checkers.py (grep for "handle_unauthorized_access", sorry old Trac  
version with no line-number support...)
[4] http://trac.turbogears.org/turbogears/browser/branches/1.0/ 
turbogears/widgets/forms.py#L194


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"TurboGears" 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/turbogears
-~----------~----~----~----~------~----~------~--~---

Reply via email to