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 !
Comments (0)