If you benefit from web2py hope you feel encouraged to pay it forward by contributing back to society in whatever form you choose!
1) Creating the table entry
First we need to create a record with minimal data on it upon which we can trigger updates. Suppose you want to create an entry on a table called 'task', you could do something like this..
 
in app/controllers/task.py :
def new():
  tableName='task'
  createForm=SQLFORM(db[tableName], 
                     fields=[field for field in db[tableName].fields if db[tableName][field].required])
                     
  if createForm.process().accepted:
    updateForm=crud.update(db[tableName],createForm.vars.id)
    response.view='task/updform.html'
    response.flash = T('task created')
    return dict(form=updateForm)    
  
  elif createForm.errors:
      response.flash = T('form has errors')
 
with a view in app/views/task/new.html :
{{=createForm}}
and another view 'task/updform.html' (displayed further below)
  
2) creating an updateTableService:
Now, it's where the interesting stuff happens. OnFocusOut will trigger AJAX requests to trigger updates on our table as the user fills each field. So, we need to add a function for this purpose as the following.
 
in app/controllers/task.py :
SUCCESS='OK'
INVALID_PARAMS='INVALID_PARAMS'
GENERIC_ERROR='UNKNOWN'
DB_ERROR='FAIL'
INVALID_DB_PARAMS=INVALID_PARAMS # should only be edited for debug purposes
UPD_TABLES_ALLOWED=['task'] # for security: update this only to include the tables where you want to allow this behaviour

@auth.requires_login()  # don't forget to adjust auth/auth to be more restrictive for your specific use case
def updateTableService():
  retValue=None
  
  if len(request.vars) <6 or not set(['tableName','rowId','fieldName','fieldValue','_formName']).issubset(set(request.vars.keys())): # basic input validation
    return INVALID_PARAMS

  tableName,rowId,fieldName,fieldValue=request.vars['tableName'],request.vars['rowId'], request.vars['fieldName'], request.vars['fieldValue']
  if not (tableName in UPD_TABLES_ALLOWED and fieldName in db[tableName].fields()):  # db params check
    return INVALID_DB_PARAMS

  formKey='_formkey[%s]' % (request.vars._formName)
  if session.has_key(formKey) and request.vars.has_key('_formKey') and request.vars._formKey in session[formKey]: # CSRF check
    retValue = db(db[tableName].id==int(rowId)).validate_and_update(**{fieldName:fieldValue}) 
    db.commit()
    
    if(len(retValue['errors'].keys()) > 0):
      response.flash = T('%s input field: %s' % (retValue['errors'].keys()[0],retValue['errors'].values()[0]))
      return DB_ERROR
      
    elif (retValue['updated'] > 0):
      response.flash = T('value updated!')
      return SUCCESS
  
  response.flash = T('Unauthorized request')
  return GENERIC_ERROR
  
and a view 'updform.html' to trigger it as follows.
 
in app/views/task/updform.html :
{{extend 'layout.html'}}


{{=form}}


<script>
jQuery(':input').focusout(function(){
inputField = $(this)
var name = inputField.attr("name");
var value = inputField.val();


var dataObj = {};
dataObj['fieldName']=name
dataObj['fieldValue']=value
dataObj['tableName']=inputField.closest('form')[0]._formname.value.split('/')[0]
dataObj['rowId']=inputField.closest('form')[0]._formname.value.split('/')[1]
dataObj['_formKey']=inputField.closest('form')[0]._formkey.value
dataObj['_formName']=inputField.closest('form')[0]._formname.value
        $.ajax({
            cache: false,
            url: '{{=URL('updateTableService')}}',
            type: 'post',
            data: dataObj,
            }).done(function(responseText){


            if(responseText=='OK'){
                inputField.css('backgroundColor','green');
                inputField.css('color','white');
                $(inputField).animate({backgroundColor: "#E9F5EB", color: '#333'});
                inputField.css('background-color','inherit');
                inputField.css('color','inherit');
}       else{
                inputField.css('backgroundColor','red');
                inputField.css('color','white');
                $(inputField).animate({backgroundColor: "#FAEFDE", color: '#333'});
                inputField.css('background-color','inherit');
                inputField.css('color','inherit');
}


            }).fail(function(responseText){
                inputField.css('backgroundColor','red');
                inputField.css('color','white');
                $(inputField).animate({backgroundColor: "#FAEFDE", color: '#333'},1000);
                inputField.css('background-color','inherit');
                inputField.css('color','inherit');


            });
        });
</script>
3) Just one more thing...
The code above, should already work in the ideal world but what if the user interrupted his session and wants to pick it up where he or she left off? We can add a function for that which you can link with baseUrl/tableName/rowId request args, as follows:
 
in app/controllers/task.py :
def updForm():
  if len(request.args) != 2:
    return INVALID_PARAMS
  tableName=request.args(0, cast=str)
  
  if not (tableName in db.tables):  # db params check
    return INVALID_DB_PARAMS
  else:
    form=crud.update(db[tableName],request.args(1, cast=int))
    return dict(form=form)
  return GENERIC_ERROR
Note: I'm relaying on the jQuery color lib for the styling. If you want to do the same make sure you download it from http://code.jquery.com/color/jquery.color-2.1.2.min.js , and add..
 
in views/web2py_ajax.html:
...
response.files.insert(4,URL('static', 'js/jquery.color-2.1.2.min.js'))
...

Try it !

Related slices

Comments (0)


Hosting graciously provided by:
Python Anywhere