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


Reply via email to