Replying to my own question:
1) 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:
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 'task/new.html' with:
and another view 'task/updform.html' (displayed further below)
2) 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:
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']
@auth.requires_login()
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:
{{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 scenario but what if the user interrupted the 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:
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
Please, let me know what you think, problems, suggestions and if you like it.
Cheers,
Francisco Ribeiro