On Sep 16, 2010, at 10:51 AM, Michael Ellis wrote: > Thanks Massimo (and Jonathan), > > I think the complexity is not so much of a problem. As you noted, it works > very well. It's just that it does so much that a bit more explanation is > needed so new users can understand how it works. > > I'd be in favor of putting your detailed description (plus a bit of > explanation about how the controller gets called twice) in the Forms and > Validation chapter, somewhere near the sentence that begins "The full > signature of the accepts method is the following:" > > Cheers, > Mike
Massimo's note raises another matter that can be confusing. I was describing FORM.accepts(), which I tend to use quite a bit, and is somewhat simpler than SQLFORM.accepts(), which calls it (though it still does all the validation work). The idea is still the same, and the API pretty much as well. > > > On Thu, Sep 16, 2010 at 1:26 PM, mdipierro <[email protected]> wrote: > assuming form=SQLFORM(db.table,record_id) > > > form.accepts(request.vars,session,formname='xxx',hideerror=False,dbio=True) > > does the following > 1) if not request.vars._formname=='xxx' then form is not being self > submitted, generate a CSRF-prevention security key and store it in > session, return False > 2) if not request.vars._formkey==CSRF-key-in-session, this is a double > form submission or a a CSRF attack, return False > 3) use the depth first tree traversal algorithm to find all the INPUT, > SELECT and TEXTAREA objects in the form, check if they have a > corresponding entry in request.vars and validate it accordingly. If it > does not pass validation store the error in form.errors[fieldname] and > modify the form by injecting error messages in it (unless > hideerror=True). When validating each INPUT object, all validators are > executed in order, if one passes, it filters request.vars.fieldname > into form.vars.fieldname > 4) for SQFORMS if dbio==True (default) and there are no form.errors it > does a db.table.insert(**form.vars) or > db(db.table.id==record_id).update(**form.vars) depending on whether > this is a create for or an update form (determined by the presence of > the record_id in form definition). If this is a create form, the id > returned by insert is stored in form.vars.id > 5) If here and no errors return True > > This is a nutshell. It is a bit more complicated because it needs to > deal with the odd behavior of select and checkboxes vs text input, > repeated fields names, the presence of Widgets which may override the > field validators and the way errors are to be displayed. > > SQLFORM.accepts calls FORM.accepts (very similar to each other) are > the most complex piece of code in web2py. Not the one I am most happy > with. If I were to re-do it I would redo it differently (in particular > because of how widgets are dealt with) but the API is clean and it > works well. > > Massimo > > On Sep 16, 12:08 pm, Michael Ellis <[email protected]> wrote: > > Thanks, Jonathan. That's approximately what I had reasoned it must be > > doing, but couldn't state as clearly as you have. > > > > So, if I'm now understanding it correctly, form.accepts() is roughly > > equivalent to the following conditions: > > > > (request contains submitted form elements) and > > (form key matches) and > > (all elements pass validation) > > > > The 'magic' is simply that clicking the submit button sends a second request > > to the controller that created it. So the form object only *seems* to > > persist; in actuality, it's recreated from scratch when the second request > > comes in. > > > > All of which explains why I was unable to store extra info in the form > > object at view time and have it available after form.accepts() came back > > True. > > > > Is that a fair summary of how it works? > > > > On Thu, Sep 16, 2010 at 11:04 AM, Jonathan Lundell > > <[email protected]>wrote: > > > > > On Sep 16, 2010, at 7:26 AM, Michael Ellis wrote: > > > > > > I was never much bothered by either of those issues, but there is one > > > > bit of 'magic' that did (and to some extent still does) confuse me. > > > > I'm wondering if it might be worthwhile to give an detailed > > > > explanation what goes on in form.accepts(). > > > > > > When I first saw the idiom > > > > > > if form.accepts(): > > > > ... > > > > return dict(form=form) > > > > > > my assumption was that the call to form.accepts() was somehow > > > > rendering and transmitting the page and waiting for a user response. > > > > But then, I wondered how that could be since the form isn't passed to > > > > the view until the return() is executed. Then I tried tracing a > > > > controller and saw that it was being called multiple times. I got > > > > lost down in the gluon code and more or less gave up trying to find > > > > out exactly how the rabbit gets into the hat. Now I just use it and > > > > trust that it works, but it still bothers me not to know what's going > > > > on under the hood. > > > > > I've had the same reaction. The key to my understanding form.accepts, > > > fwiw, > > > was reading accepts itself. Keep in mind that web2py encourages form > > > self-submission. That implies that the same code (controller/function) > > > typically needs to run twice: once to create the page+form for display, > > > and > > > again to handle the form's submission. > > > > > So form.accepts() is telling you whether it's the first time through, form > > > creation (False) or the second, form submission (True). It's a little more > > > complicated than that because of validation and error handling, but that's > > > it in a nutshell. > > > > >

