Time for action getting the time with AJAX

Enter the following code and run it. If you point your web browser to the familiar http://localhost:8080 address, you will see something similar to the picture below with the time changing every five seconds or so. (The code is also available as timer.py)

Time for action getting the time with AJAX

What just happened?

Our small CherryPy application offers just two methods (both highlighted in the code). The index() method returns a minimalistic HTML page with some static text and a small piece of JavaScript that takes care of retrieving the current time from the server. It also features a time() method that simply returns the current time as plain text.

Chapter4/timer.py

import cherrypy
import os.path
from time import asctime
current_dir = os.path.dirname(os.path.abspath(__file__))
class Root(object):
	@cherrypy.expose
	def index(self):
		return '''<html>
		<head><script type="text/javascript" src="/jquery.js" ></script></
head>
		<body><h1>The current time is ...</h1><div id="time"></div>
		<script type="text/javascript">
		window.setInterval(function(){$.ajax({url:"time",cache:false,success:
function(data,status,request){
			$("#time").html(data);
		}});},5000);
		</script>
		</body>
		</html>'''
	@cherrypy.expose
	def time(self,_=None):
		return asctime()
cherrypy.quickstart(Root(),config={
	'/jquery.js':
	{ 'tools.staticfile.on':True,
	'tools.staticfile.filename':os.path.join(current_
dir,"static","jquery","jquery-1.4.2.js")
	}
})

The magic is in that small piece of JavaScript (highlighted). This script is executed once the static page is loaded and it calls the setInterval() method of the window object. The arguments to the setInterval() method are an anonymous function and a time interval in milliseconds. We set the time interval to five seconds. The function passed to setInterval() is called at the end of each interval.

In this example, we pass an anonymous function to setInterval() that relies on jQuery's ajax() function to retrieve the time. The ajax() function's only argument is an object that may contain numerous options. The url option tells which URL to use to retrieve the data from, in this case, the relative URL time (relative to the page that serves the content the script is embedded in, /, so it actually refers to http://localhost:8080/time).

The cache option is set to false to prevent the browser from using a cached result when instructed to get the time URL it has seen already. This is ensured by the underlying JavaScript library by appending an extra _ parameter (that is the name of this parameter which consists of a single underscore) to the URL. This extra parameter contains a random number, so the browser will regard each call as a call to a new URL. The time() method is defined to accept this parameter because otherwise CherryPy would raise an exception, but the contents of the parameter are ignored.

The success option is set to a function that will be called when the data is successfully received. This function will receive three arguments when called: the data that was retrieved by the ajax() function, the status, and the original request object. We will only use the data here.

We select the<div> element with the time ID and replace its contents by passing the data to its html() method. Note that even though the time() method just produces text, it could just as easily have returned text containing some markup this way.

We explicitly instructed the ajax() function not to cache the result of the query, but instead we could also decorate our time() method with CherryPy's expires tool. This would instruct the time() method to insert the correct http headers in response to instruct the browser not to cache the results. This is illustrated in the following code (available in timer2.py):

@cherrypy.tools.expires(secs=0,force=True)
	@cherrypy.expose
	def time(self,_=None):
		return asctime()

Using the @cherrypy.tools.expires decorator means we do not have to instruct the ajax() method not to cache the result, which gives us the option to use a shortcut method. The JavaScript code may then be rewritten to use jQuery's load() method, shown as follows:

<script type="text/javascript">
window.setInterval(function(){$("#time").load("time");},5000);
</script>

The load() method is passed the URL where it will retrieve the data and, upon success, will replace the contents of the selected DOMelement with the data it received.

Note

jQuery provides many AJAX shortcut methods and all these methods share a common set of defaults that may be set using the ajaxSetup() function. For example, to make sure all AJAX methods will not cache any returned result, we could call it like this: $.ajaxSetup({cache:false});

Redesigning the Tasklist application

The tasklist application will consist of two parts: an authentication part for which we will reuse the LogonDB class and new TaskApp class. The TaskApp class will implement the methods necessary to deliver the page with an overview of all tasks for the authenticated user plus additional methods to respond to AJAX requests.

Instead of a filesystem, SQLite will be used to store the tasks for all users. Note that this is a separate database from the one used to store usernames and passwords. Such a setup allows us to keep the authentication functionality separate from other concerns, allowing for easier reuse. Once the user is authenticated, we do, of course, use his/her username to identify the tasks belonging to him/her.

Access to the task database will be encapsulated in a tasklistdb module. It provides classes and methods to retrieve, add, and modify tasks for a given user. It is not concerned with checking access permission, as this is the responsibility of the TaskApp class. You can picture this separation as a two layer model, the top layer checking user credentials and serving content, and the bottom layer actually interfacing with a database.

Redesigning the Tasklist application

Database design

The design of our task database (the database schema) is very straightforward. It consists of a single table, which contains columns to define a task.

Task

    

task_id

description

duedate

completed

user_id

integer

primary key

autoincrement

    

Most columns do not have a specific type defined, as SQLite will let us store anything in a column. Furthermore, most columns do not have special constraints except for the task_id column that we designate to be the primary key. We do explicitly type the task_id column as an integer and designate it as autoincrement. This way, we do not have to set the value of this column explicitly, but a new unique integer will be inserted for us every time we add a new task to the table.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.216.75