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 -~----------~----~----~----~------~----~------~--~---

