Is there a way to differentiate between a form refresh (F5) and a submit that calls back the form?

82 views
Skip to first unread message

João Matos

unread,
Mar 26, 2019, 5:55:50 PM3/26/19
to web2py-users
Is there a way to differentiate between a form refresh (F5) and a submit that calls back the form?

Anthony

unread,
Mar 26, 2019, 10:21:07 PM3/26/19
to web2py-users
What are you really trying to do?

João Matos

unread,
Mar 27, 2019, 6:40:26 AM3/27/19
to web2py-users
I have a SQLFORM.factory where I edit a record from a grid (the record is selected in the grid and the user presses a button created with the grid's selectable).
At the start of the edit function I read the record in question from the database to fill the SQLFORM.factory fields.
If the user tries to save the record and the record was changed while he was editing, I show a form.errors message.
At this point I have 2 options:
1. The user is informed to press F5 to see the changes. This is great to view the changes but if the user tries to save the record, and because the db record is read at the start of the edit function, my function was "thinks" the record has been changed while editing and doesn't allows him to save. This is because I'm comparing the current db record modified_on with the original (grid's row) modified_on.
2. The user is informed to go back to grid and re-select the record to see the changes and continue editing. This works but is a bad user experience.

What I would like is to differentiate between an F5 page refresh and a form check (call back).

Anthony

unread,
Mar 27, 2019, 7:10:07 AM3/27/19
to web2py-users
Please show the code.

João Matos

unread,
Mar 27, 2019, 8:50:56 AM3/27/19
to web2py-users
I was able to solve it with the SQLFORM.factory hidden parameter.
Thanks.

Anthony

unread,
Mar 27, 2019, 2:39:16 PM3/27/19
to web2py-users
On Wednesday, March 27, 2019 at 8:50:56 AM UTC-4, João Matos wrote:
I was able to solve it with the SQLFORM.factory hidden parameter.

OK, but note that there is no need to have the user refesh the page to proceed. 

João Matos

unread,
Mar 27, 2019, 2:47:01 PM3/27/19
to web2py-users
You mean to update the editing form with the new values from the record on the db without telling the user to press F5?
How can I do it?

Thanks.

Anthony

unread,
Mar 27, 2019, 2:47:33 PM3/27/19
to web2py-users
Need to see your code.

João Matos

unread,
Mar 27, 2019, 3:06:16 PM3/27/19
to web...@googlegroups.com
This is my code for the another table (it has the same situation, but without using a SQLFORM.factory).
I posted this instead of the SQLFORM.factory, because it is simpler code.

My index function contains the grid

        grid = SQLFORM.grid(
            query
,
            csv
=False, deletable=False, details=False,
            links
=[
               
lambda row: A(
                    SPAN
(T('Delete'), _class='buttontext button', _title='Delete'),
                    _href
=URL('get_approval', args=[row.id], user_signature=True),
                    _class
='button btn btn-default btn-secondary',
               
),
           
],
            onvalidation
=on_validation,  # Form only.
           
orderby=db.client.name,
            paginate
=session.auth.user.pagination,
           
# represent_none='',  # Grid and view form only.
       
)


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

My onvalidation function

    if request.args and request.args[0] == 'new':
       
# Fix for SQLite instead of for_update=True. When for_update is
       
# fixed for SQLite, only the first SELECT in the function should
       
# have for_update=True.
       
while True:
           
try:
               
# SQLite only does database lock.
                db
.executesql('BEGIN IMMEDIATE TRANSACTION')
               
break
           
except sqlite3.OperationalError:
                sleep
(0.5)
   
elif not db.client(request.vars.id).is_active:
        session
.flash = T('Record was deleted while you were editing.')
        redirect
(URL(user_signature=True))
   
elif request.post_vars.modified_on != str(db.client(request.vars.id).modified_on):
        form
.errors.code = T('Record was changed while you were editing. '
                             
'Press F5 to refresh and press Resend in the '
                             
'browser question.')
   
elif form.vars.delete_this_record:
       
# Fix for SQLite instead of for_update=True. When for_update is
       
# fixed for SQLite, only the first SELECT in the function should
       
# have for_update=True.
       
while True:
           
try:
               
# SQLite only does database lock.
                db
.executesql('BEGIN IMMEDIATE TRANSACTION')
               
break
           
except sqlite3.OperationalError:
                sleep
(0.5)

        db
.client(request.vars.id).update_record(
            cancel_approved_by
=auth.user_id,
            canceled_by
=auth.user_id,
            canceled_on
=request.now,
            is_active
=False,
       
)

        db
.commit()

        session
.flash = T('Done.')
        redirect
(URL(user_signature=True))
   
elif request.args and request.args[0] == 'edit':
       
# Fix for SQLite instead of for_update=True. When for_update is
       
# fixed for SQLite, only the first SELECT in the function should
       
# have for_update=True.
       
while True:
           
try:
               
# SQLite only does database lock.
                db
.executesql('BEGIN IMMEDIATE TRANSACTION')
               
break
           
except sqlite3.OperationalError:
                sleep
(0.5)

Anthony

unread,
Mar 27, 2019, 9:57:29 PM3/27/19
to web2py-users
I don't see any code that checks for record change, but you could do something like:

elif request.args and request.args[0] == 'edit':

   
if request.vars.modified_on != str(form.record.modified_on):
        session
.flash = 'Record change detected. Please try again.'
        redirect
(URL(args=request.args, vars=request.get_vars))

That will redirect to the same URL, which will load the same record (with the updated data), and show the message after redirect.

Anthony

João Matos

unread,
Mar 28, 2019, 6:49:37 AM3/28/19
to web...@googlegroups.com
Thanks for the tip.

I was using this (it's included in the above code)

  elif request.post_vars.modified_on != str(db.client(request.vars.id).modified_on):
     form
.errors.code = T('Record was changed while you were editing. '
                         
'Press F5 to refresh and press Resend in the '
                         
'browser question.')

which I changed now to this

  elif request.post_vars.modified_on != str(db.client(request.vars.id).modified_on):
     session
.flash = T('Record was changed while you were editing. '
                      
'This is the updated record.')
     redirect
(URL(args=request.args, vars=request.get_vars, user_signature=True))

Anthony

unread,
Mar 28, 2019, 9:01:46 AM3/28/19
to web2py-users
which I changed now to this

  elif request.post_vars.modified_on != str(db.client(request.vars.id).modified_on):
     session
.flash = T('Record was changed while you were editing. '
                      
'This is the updated record.')
     redirect
(URL(args=request.args, vars=request.get_vars, user_signature=True))

Why not put the above inside the "edit" condition, as it is not relevant otherwise (and won't work on record creation)? Also, given that this is in the onvalidation function, why not use form.record rather than unnecessarily re-selecting the record via db.client(request.vars.id)?

Anthony

João Matos

unread,
Mar 28, 2019, 9:43:49 AM3/28/19
to web2py-users
Made the changes you suggested. Thanks Anthony.

This is an example of my new onvalidation, just in case you have other tips :)

def on_validation(form: SQLFORM) -> None:
   
"""Used in the edit form, including when creating and deleting.

    :param form: Form.
    """

   
# Fix for SQLite instead of for_update=True. When for_update is
   
# fixed for SQLite, only the first SELECT in the function should
   
# have for_update=True.
   
while True:
       
try:
           
# SQLite only does database lock.
            db
.executesql('BEGIN IMMEDIATE TRANSACTION')
           
break
       
except sqlite3.OperationalError:
            sleep
(0.5)

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

       
if db.wo_counter(year_=request.now.year):
            form
.errors.last_assigned = T('Counter already exists')
   
else:  # Edit/delete.
       
if (not form.record.is_active and not form.vars.delete_this_record
               
and auth.has_membership(SUPERVISOR_ROLE)):
            db
.wo_counter(request.vars.id).update_record(
                cancel_approved_by
=None,
                canceled_by
=None,
                canceled_on
=None,
                is_active
=True,
           
)

            db
.commit()

            session
.flash = T('Record was activated.')
            redirect
(URL('index', user_signature=True))
       
elif not form.record.is_active:

            session
.flash = T('Record was deleted while you were editing.')

            redirect
(URL('index', user_signature=True))
       
elif (request.post_vars.modified_on != str(form.record.modified_on)):

            session
.flash = T('Record was changed while you were editing. '
                             
'This is the updated record.')
            redirect
(URL(args=request.args, vars=request.get_vars, user_signature=True))

       
elif db.wo_counter(request.args[-1]).last_assigned > form.vars.last_assigned:
            form
.errors.last_assigned = T('New value must >= than old.')
       
elif form.vars.delete_this_record:
            db
.wo_counter(request.vars.id).update_record(

                cancel_approved_by
=auth.user_id,
                canceled_by
=auth.user_id,
                canceled_on
=request.now,
                is_active
=False,
           
)

            db
.commit()

            session
.flash = T('Done.')
            redirect
(URL(user_signature=True))

João Matos

unread,
Mar 28, 2019, 2:58:30 PM3/28/19
to web...@googlegroups.com
Had to revert this

elif request.post_vars.modified_on != str(form.record.modified_on):

    session
.flash = T('Record was changed while you were editing. '
                     
'This is the updated record.')
    redirect
(URL(args=request.args, vars=request.get_vars, user_signature=True))

to this

elif session.client_modified_on != form.record.modified_on:
    session.client_modified_on = form.record.modified_on
    session.flash = T('Record was changed while you were editing. '
                     
'This is the updated record.')
    redirect
(URL(args=request.args, vars=request.get_vars, user_signature=True))

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

to this

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

       
if not session.client_modified_on:
            session
.client_modified_on = form.record.modified_on
   
elif 'edit' not in request.args and session.client_modified_on:
       
del session.client_modified_on

because if the record was changed by another user while the this user was editing and the this user made a mistake in a field that was auto-checked by web2py (using the requires from the model file), the upper version would not catch that the record had been changed and would save the record instead of showing the message.


quinta-feira, 28 de Março de 2019 às 13:01:46 UTC, Anthony escreveu:
Reply all
Reply to author
Forward
0 new messages