SQLFORM.grid w/ selectable generates multiple requests

58 views
Skip to first unread message

Scott Hunter

unread,
Sep 21, 2016, 9:36:14 AM9/21/16
to web2py-users
Here's a simple controller using an SQLFORM.grid w/ selectable:

    def open_requests():
       
print 'Req: %r' % request.vars
        query
= ...
        form
= SQLFORM.grid(query, csv=False, details=False, deletable=False, editable=False, searchable=False, create=False,
            selectable
= lambda ids : None)
       
return locals()

This, as expected, puts a checkbox on each row.  If I select two rows and hit Submit, I get the following printed from the server:

Req: <Storage {'records': ['142', '196'], '_formkey': '0720df2b-6449-43c8-a6b7-6c89dfdebcd3', '_signature': 'cbc73b5fbdc8e5961830efbe3f672f35a41d6771', '_formname': 'web2py_grid'}>
Req: <Storage {'_signature': 'cbc73b5fbdc8e5961830efbe3f672f35a41d6771'}>

The section of the online book about this says the lambda in the selectable should be a redirect; when it was, I still got 2 requests, but the second had an added var w/ the selected IDs.

Is this the correct behavior?  If so, what is the reasoning behind it, and did I miss the explanation in the book?  

I'm using version 2.14.6

Scott Hunter

unread,
Sep 21, 2016, 9:54:05 AM9/21/16
to web2py-users
If I have the controller function return a string if there is a 'records' variable, the second request is not made; however, then it does not redisplay the form.

Anthony

unread,
Sep 21, 2016, 12:37:52 PM9/21/16
to web2py-users
On Wednesday, September 21, 2016 at 9:36:14 AM UTC-4, Scott Hunter wrote:
Here's a simple controller using an SQLFORM.grid w/ selectable:

    def open_requests():
       
print 'Req: %r' % request.vars
        query
= ...
        form
= SQLFORM.grid(query, csv=False, details=False, deletable=False, editable=False, searchable=False, create=False,
            selectable
= lambda ids : None)
       
return locals()

This, as expected, puts a checkbox on each row.  If I select two rows and hit Submit, I get the following printed from the server:

Req: <Storage {'records': ['142', '196'], '_formkey': '0720df2b-6449-43c8-a6b7-6c89dfdebcd3', '_signature': 'cbc73b5fbdc8e5961830efbe3f672f35a41d6771', '_formname': 'web2py_grid'}>
Req: <Storage {'_signature': 'cbc73b5fbdc8e5961830efbe3f672f35a41d6771'}>

The section of the online book about this says the lambda in the selectable should be a redirect; when it was, I still got 2 requests, but the second had an added var w/ the selected IDs.

The book doesn't say the callback should do a redirect -- that's just an example. In any case, if your callback doesn't do a redirect, then the grid code itself will do a redirect back to the original URL. This is by design (maybe so reloading the page or leaving and returning don't trigger a browser warning and subsequent re-posting of the selected rows).

Anthony

Scott Hunter

unread,
Sep 21, 2016, 1:16:29 PM9/21/16
to web2py-users
I think I understand; seems like something that should be documented, as well as able to be disabled.

Anthony

unread,
Sep 21, 2016, 3:19:15 PM9/21/16
to web2py-users
On Wednesday, September 21, 2016 at 1:16:29 PM UTC-4, Scott Hunter wrote:
I think I understand; seems like something that should be documented, as well as able to be disabled.

Is it causing a problem for you? Why do you need to disable the redirect?

Anthony

Anthony

unread,
Sep 21, 2016, 10:42:59 PM9/21/16
to web2py-users

The section of the online book about this says the lambda in the selectable should be a redirect; when it was, I still got 2 requests, but the second had an added var w/ the selected IDs.

The book doesn't say the callback should do a redirect -- that's just an example. In any case, if your callback doesn't do a redirect, then the grid code itself will do a redirect back to the original URL. This is by design (maybe so reloading the page or leaving and returning don't trigger a browser warning and subsequent re-posting of the selected rows).

Also, in case the selectable callback modifies or deletes any records, reloading the grid ensures it will be displaying up-to-date data.

Anthony

Scott Hunter

unread,
Sep 22, 2016, 6:45:55 AM9/22/16
to web2py-users
Because I don't need it.  The original call tells me enough to make the modifications I need to do, and then proceeds to build an up-to-date grid, which becomes wasted effort because I'm getting a redirect re-build the page I just built.

- Scott

Anthony

unread,
Sep 22, 2016, 2:14:41 PM9/22/16
to web...@googlegroups.com
On Thursday, September 22, 2016 at 6:45:55 AM UTC-4, Scott Hunter wrote:
Because I don't need it.  The original call tells me enough to make the modifications I need to do, and then proceeds to build an up-to-date grid, which becomes wasted effort because I'm getting a redirect re-build the page I just built.

The grid code first retrieves the records and then calls the selectable callback (which it must do, as the record ids are passed to the callback), so if the callback makes any modifications to the records, the resulting grid will not contain up-to-date data unless the grid is called again (hence the redirect). I think the idea is to ensure it works in all cases, though I agree it could be made more efficient in some cases.

For now, a workaround is to check for request.post_vars.records, and if present, run whatever code you need to, and then delete request.post_vars.records and request.post_vars._formname before creating the grid (this will prevent the form from being accepted and therefore skip the redirect). You could abstract this into a wrapper around the grid.

Anthony

Anthony

unread,
Sep 22, 2016, 2:24:29 PM9/22/16
to web...@googlegroups.com
On Thursday, September 22, 2016 at 2:14:41 PM UTC-4, Anthony wrote:
On Thursday, September 22, 2016 at 6:45:55 AM UTC-4, Scott Hunter wrote:
Because I don't need it.  The original call tells me enough to make the modifications I need to do, and then proceeds to build an up-to-date grid, which becomes wasted effort because I'm getting a redirect re-build the page I just built.

The grid code first retrieves the records and then calls the selectable callback (which it must do, as the record ids are passed to the callback), so if the callback makes any modifications to the records, the resulting grid will not contain up-to-date data unless the grid is called again (hence the redirect). I think the idea is to ensure it works in all cases, though I agree it could be made more efficient in some cases.

For now, a workaround is to check for request.post_vars.records, and if present, run whatever code you need to, and then delete request.post_vars.records and request.post_vars.formname before creating the grid (this will prevent the form from being accepted and therefore skip the redirect). You could abstract this into a wrapper around the grid.

Note, with the above method, you would be foregoing the web2py form processing functionality, which among other things protects against CSRF. If you want to allow the grid to handle the form processing and callback as usual but still short circuit the redirect, you could do something like this:

def mygrid(*args, **kwargs):
    if 'records' in request.post_vars:
       
try:
           
return SQLFORM.grid(*args, **kwargs)
       
except HTTP:
           
del request.post_vars.records
           
del request.post_vars._formname
           
return SQLFORM.grid(*args, **kwargs)
    else:
        return SQLFORM.grid(*args, **kwargs)

def myfunc():
   
return dict(grid=mygrid(..., selectable=mycallback))

Anthony
Reply all
Reply to author
Forward
0 new messages