Time for action serving HTML as dynamic content

We have seen how to run an application and access it with a web browser, now let's have a look at the Python code needed to accomplish this. We will need to serve static files but in addition to those static files we want to generate the main HTML content dynamically. This isn't strictly necessary as we could have served it as a static file just as easily but it serves as a simple example of how to generate dynamic content:

Chapter2/nocontent.py

import cherrypy
import os.path
current_dir = os.path.dirname(os.path.abspath(__file__))

You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

nocontent.py starts off with importing the cherrypy and os.path modules. The latter is needed to determine the directory that nocontent.py resides in (highlighted), so that we may refer to other static files and directories relative to nocontent.py. This way, we make life a lot easier once we want to move this application to its final destination on a production server.

Chapter2/nocontent.py

class Root(object): ... <omitted> ...
if __name__ == "__main__":
	cherrypy.quickstart(Root(),config={
		'/static':
		{ 'tools.staticdir.on':True,
			'tools.staticdir.dir':os.path.join(current_dir,"static")
		}
	})

What just happened?

The next step is to start the CherryPy server with the quickstart() function (highlighted). We pass two arguments: the first one is an object instance of a class that exposes some methods to CherryPy that may deliver dynamic content. We will look at that one in a minute.

The second (named) argument is a dictionary containing a number of configuration items. In this case, we configure just a static directory, but in other situations, additional configuration items may appear here. The URL component /static is made to refer to a location on the file-system relative to nocontent.py by concatenating to the current_dir determined earlier. Again we use a function from Python's os.path module, os.path.join(), to create a file path in a platform-independent manner.

The static directory contains all jQuery and jQuery UI files we will need for this application along with all CSS files and images to style the application. In this example, without real content there are no additional files besides the ones belonging to the jQuery and jQuery UI libraries, but if we needed them, we could have placed them here.

Note

If we would like CherryPy to listen on a different port, we should indicate this in the global configuration. This can be done by preceding the call to cherrypy.quickstart() with cherrypy.config.update({'server.socket_port':8088}). CherryPy has a rich palette of configuration options and can even be instructed to read its configuration from files. A good starting point for all the possibilities is http://www.cherrypy.org/wiki/ConfigAPI.

We still have to implement a Root class to provide CherryPy with an object instance that may act as the root of the document hierarchy that CherryPy may serve. This class should actually be defined before we can create an instance to pass to the quickstart() method, but I wanted to concentrate on how to start the server first before concentrating on producing content:

Chapter2/nocontent.py

class Root(object):
	content = '''... <omitted> ...'''
	@cherrypy.expose
	def index(self):
			return Root.content

This Root class contains a single class variable content that holds the HTML code we will serve. We will examine it in detail in the next section. This HTML is generated by the index() method and passed to the HTTP server that in its turn will pass it on to the requesting browser.

It is exposed to CherryPy by the @cherrypy.expose decorator (highlighted). Only exposed methods will be called by CherryPy to produce content. In the default configuration, CherryPy will map a URL of the form /name to a method called name(). A URL containing just a forward slash / will map to a method called index(), just like the one we defined here. This means we have now configured CherryPy to deliver dynamic content when the user directs his browser to http://127.0.0.1:8080/ (and he may even omit the final slash as CherryPy effectively ignores a trailing slash by default).

Note that we let index() return the contents of a single string variable but we could have returned just about anything, making this a truly dynamic way of producing content.

Who serves what: an overview

Serving an application from a mixture of dynamic and static content may quickly become confusing. It might help to form a clear picture early on of the relations between components, of data streams, and directory structures used. This builds on the general picture sketched in Chapter 1 and will get extended in each chapter.

Almost all applications in this book are served from the same directory structure:

Who serves what: an overview

The top-level directory contains one or more Python files that you can execute and that will start a CherryPy server. Those Python files implement the server-side of an application. They may import additional modules from the same top-level directory.

The top-level directory also contains a subdirectory called static. It holds several JavaScript files, including the jQuery and jQuery UI libraries and any additional plugins. It also contains a directory called css that contains one or more subdirectories with additional CSS stylesheets and images for jQuery UI themes.

Note that although our applications are served by a web server, there are no HTML files to be seen because all HTML content is generated dynamically.

From an application point of view, the best way to comprehend a web application is to see the application as distributed. Some of its code (in our case Python) runs on the server, while other code (JavaScript) runs in the browser. Together, they make up the complete application as visualized in the following image:

Who serves what: an overview

Pop quiz serving content with CherryPy

We made the choice to serve our content from the index() method so users could get the content by referring to the URL ending in just a slash (/). But what if we would like our content to be accessed by referring to a URL like http://127.0.0.1/content? What would have to change?

HTML: separating form and content

Almost always, it is a good idea to separate form and content. This enables us to concentrate on the logical structure of the information we want to present and makes it easier to change the appearance of the data later. This even allows for applying themes in a maintainable way.

The structure of our data is laid down in the HTML we deliver to the browser. To be more precise, the structural data can be found within the<body> element, but the<head> element of the HTML contains important information as well. For example, references to stylesheets and JavaScript libraries that will be used to style the appearance of the data and enhance the user interaction.

In the following code, we use a<link> element to refer to a CSS stylesheet from a theme we downloaded from the jQuery UI website (highlighted). In this example, we do not actually use this stylesheet and nor are the jQuery and jQuery UI libraries included in the<script> elements, but this example shows how to refer to those libraries from the HTML we produce, and in the following examples, we will see that this is also the spot where we refer to any additional JavaScript libraries that we will create ourselves. The actual content is enclosed in the highlighted<div> element.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<link rel="stylesheet"
href="static/css/redmond/jquery-ui-1.8.1.custom.css"
type="text/css" media="screen, projection" />
<script type="text/javascript"
	src="static/jquery-1.4.2.js" ></script>
<script type="text/javascript"
	src="static/jquery-ui-1.8.1.custom.min.js" ></script>
</head>
<body id="spreadsheet_example">
<div id="example">an empty div</div>
</body>
</html>
..................Content has been hidden....................

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