How can I access the rows that resulted from the query made by SQLFORM.grid from onvalidation?

73 views
Skip to first unread message

João Matos

unread,
Mar 25, 2019, 10:25:26 AM3/25/19
to web2py-users
Hello,

How can I access the rows that resulted from the query made by the SQLFORM.grid from the onvalidation function?

Thanks,

JM

Dave S

unread,
Mar 25, 2019, 4:34:50 PM3/25/19
to web2py-users

I don't think you can, because the function is called before the select() or write() (but after the check for IN_DB()).
You are passed the form data.

You may want instead the db callbacks:
<URL:http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#callbacks-on-record-insert-delete-and-update>

/dps
 

João Matos

unread,
Mar 25, 2019, 4:54:46 PM3/25/19
to web2py-users
I didn't explain myself correctly.

The SQLFORM.grid does not check for record changed during the edit. Which is a shame and I requested this feature in Github.

To overcome this limitation, I want to compare the selected row from the grid (when pressing the edit button) with the current record on disk.
And I want to do this from inside the onvalidation function, because that is the only way to check before the edited record is saved. At least that I know of.
The callbacks you mentioned don't seem to help with this. Or am I missing something?

Anthony

unread,
Mar 25, 2019, 7:11:35 PM3/25/19
to web2py-users
def onvalidation(form):
    current_record
= db.mytable(request.args(-1))

Alternatively, db.mytable(request.post_vars.id).

Anthony.

João Matos

unread,
Mar 25, 2019, 7:58:56 PM3/25/19
to web2py-users
I need the records from the grid itself.
The objective is to be able to compare the modified_on field from the record of the grid with the same record on the db at the moment of the save (onvalidation) to detect if there was a record change between those 2 moments.
Like the detect_record_change of the form, but for the grid.
I found that the grid has a attribute rows which are the records and was able to make it work.

Val K

unread,
Mar 26, 2019, 6:35:57 AM3/26/19
to web2py-users
I suppose modified_on field is readonly, so it is not post back along form submitting, but you can inject it using hidden attribute, that could be passed to the grid through editargs.
I would do the following:
- detect edit action by checking request.args(-3)=='edit'
- retrieve modified_on of db.tbl[request.args[-1]]
- pass it to the grid - grid(..., editargs=dict(hidden=dict(modified_on=...)))
- get it back by request.vars.modified_on in the onvalidation callback

Anthony

unread,
Mar 26, 2019, 9:42:53 AM3/26/19
to web2py-users
On Monday, March 25, 2019 at 7:58:56 PM UTC-4, João Matos wrote:
I need the records from the grid itself.
The objective is to be able to compare the modified_on field from the record of the grid with the same record on the db at the moment of the save (onvalidation) to detect if there was a record change between those 2 moments.
Like the detect_record_change of the form, but for the grid.
I found that the grid has a attribute rows which are the records and was able to make it work.

The grid.rows object is None during requests that create and process the forms, so not sure how you could be accessing grid.rows from onvalidation during the processing of an edit form. In any case, you want to compare the submitted record with the version that was presented in the edit form (not the version that was presented in the grid, which could possibly differ). My original means of accessing the current record is not necessary, as you can actually get it via form.record within the onvalidation function. If you want to compare the modified_on field, you also need to pass that to the edit form (as a hidden field) when it is first created so the original value gets submitted back with the form. To do that, you can take Val K's approach, or to save an extra fetch of the record from the database, you can do the following:

def my_grid():
   
def onvalidation(form):
       
if request.post_vars.modified_on != str(form.record.modified_on):
            form
.errors['modified_on'] = True
            response
.flash = 'Record change detected.'

    grid
= SQLFORM.grid(db.mytable, ...)

   
if 'edit' in request.args:
        form
= grid.update_form
        form
['hidden'].update(modified_on=form.record.modified_on)

   
return dict(grid=grid)

Anthony

João Matos

unread,
Mar 26, 2019, 10:12:56 AM3/26/19
to web...@googlegroups.com
I just save the grid rows to session using this at the end of the index function (where the grid is created):

    if grid.rows:
        session
.grid_rows = grid.rows.as_dict()

And then in onvalidation function I compare the grid's row modified_on (selecting the correct row that was in the edit form), with the db record modified_on using this:

    if request.args and request.args[0] == 'new':
       
...
   
elif (session.grid_rows[int(request.vars.id)]['modified_on']
           
!= db.manual_lang(request.vars.id).modified_on):
        form
.errors.name = T('The record was changed while you were editing. '
                             
'Go back to the grid to see the updated record.')
   
else:  # Edit/delete from edit form, after checking the record was not modified while editing.
       
...

What do you think of this solution?

João Matos

unread,
Mar 26, 2019, 10:38:31 AM3/26/19
to web2py-users
@Anthony
Just realized by your solution that form has a record struct that I can use.
So the solution became much simpler.

No need to copy the grid.rows to session.

And onvalidation function became

    if request.args and request.args[0] == 'new':
       
...

    elif form.record.modified_on != db.manual_lang(request.vars.id).modified_on:

        form
.errors.name = T('The record was changed while you were editing. '
                             
'Go back to the grid to see the updated record.')
   
else:  # Edit/delete from edit form, after checking the record was not modified while editing.
       
...

Thank you all.
Feedback welcome on this solution,of course.


terça-feira, 26 de Março de 2019 às 13:42:53 UTC, Anthony escreveu:

João Matos

unread,
Mar 26, 2019, 11:03:19 AM3/26/19
to web2py-users
Spoke too soon. Doesn't work.
had to revert to my previous solution (save grid.rows to session ....).

Anthony

unread,
Mar 26, 2019, 11:12:37 AM3/26/19
to web2py-users
On Tuesday, March 26, 2019 at 11:03:19 AM UTC-4, João Matos wrote:
Spoke too soon. Doesn't work.
had to revert to my previous solution (save grid.rows to session ....).

Saving the entire grid.rows object to the session is unnecessary, as you only need the "modified_on" value of the particular record being edited (which you can get from grid.update_form.record.modified_on when the edit form is first created). It also makes more sense to save the value of modified_on when the edit form is created (as that represents the version the user ends up modifying in the form) rather than the earlier version shown in the grid.

You can store the modified_on value either in the session or in a hidden field in the form.

Anthony

João Matos

unread,
Mar 26, 2019, 12:13:05 PM3/26/19
to web2py-users
 Following your advice I changed my index function (where the grid is created) to

    if 'edit' in request.args:
        form
= grid.
update_form
        session
.modified_on = form.record.modified_on

And the onvalidation function to

    if request.args and request.args[0] == 'new':
         
...

   
elif session.modified_on != db.client(request.vars.id).modified_on:
        form
.errors.code = T('The record was changed while you were editing. '
                             
'Press F5 to refresh.')

   
else:  # Edit/delete from edit form, after checking the record was not modified while editing.
       
...

What is the more secure way to transfer the modified_on?
Using
session.modified_on
or
form['hidden'].update(modified_on=form.record.modified_on)
and then
request.post_vars.modified_on
?
I ask this because there is an option to make session secure when using HTTPS.


Anthony

unread,
Mar 26, 2019, 10:37:06 PM3/26/19
to web2py-users
What is the more secure way to transfer the modified_on?
Using
session.modified_on
or
form['hidden'].update(modified_on=form.record.modified_on)
and then
request.post_vars.modified_on
?
I ask this because there is an option to make session secure when using HTTPS.

If there is some reason you want to keep the modified_on value a secret, the session will be more secure (the user has no access to the session data). However, using the session (properly) can be more complex. First, what happens if the user opens multiple edit forms in different browser tabs? If you always use the same session key to store the modified_on value, each new form that is opened will overwrite the value of the previous form. To prevent that, you would likely want the key to include the record ID as well. But then you have to worry about deleting old keys from the session so it doesn't grow indefinitely. If you instead use a form hidden field, you don't have to worry about any of this.

Anthony
 

João Matos

unread,
Mar 27, 2019, 5:31:59 AM3/27/19
to web2py-users
Thanks for the explanation Anthony.
Reply all
Reply to author
Forward
0 new messages