HomePython Page 6 - Writing CGI Programs in Python
Putting the pieces together - Python
And now for something completely different... Python has a very extensive, well documented and portable module library that provides a large variety of useful functions. The Internet-related collection is particularly impressive, with modules to deal with everything from parsing and retrieving URL's to retrieving mail from POP servers and everything in between.
Let's review what we've covered so far. We've learned how to get information from the user via a CGI form and we've learned how to print out information in a nicely formatted HTML page. Let's put these two separate bits of knowledge together into one program that will prompt the user with a form, and then print out the information submitted by that form.
We're going to go ahead and get fancy and use two templates this time ... one, the same generic site template as before, and the other template will contain our form that will be presented to the user.
<FORM METHOD="POST" ACTION="sample1.py">
<INPUT TYPE=HIDDEN NAME="key" VALUE="process">
<INPUT TYPE=TEXT NAME="name" size=60>
<INPUT TYPE=TEXT NAME="email" size=60>
<INPUT TYPE=RADIO NAME="color" VALUE="blue" CHECKED>Blue
<INPUT TYPE=RADIO NAME="color" VALUE="yellow">No, Yellow...
<INPUT TYPE=RADIO NAME="color" VALUE="swallow">What do you
mean, an African or European swallow?
<TEXTAREA NAME="comment" ROWS=8 COLS=60>
<INPUT TYPE="SUBMIT" VALUE="Okay">
We'll use the same generic template file from before.
Here is the program itself:
# specify the filename of the template file
TemplateFile = "template.html"
# specify the filename of the form to show the user
FormFile = "form.html"
# Display takes one parameter - a string to Display
TemplateHandle = open(TemplateFile, "r") # open in read only mode
# read the entire file as a string
TemplateInput = TemplateHandle.read()
TemplateHandle.close() # close the file
# this defines an exception string in case our
# template file is messed up
BadTemplateException = "There was a problem with the HTML template."
SubResult = re.subn("<!-- *** INSERT CONTENT HERE *** -->",
if SubResult == 0:
print "Content-Type: text/html\n\n"
### what follows are our two main 'action' functions, one to show the
### form, and another to process it
# this is a really simple function
FormHandle = open(FormFile, "r")
FormInput = FormHandle.read()
# extract the information from the form in easily digestible format
name = form["name"].value
# name is required, so output an error if
# not given and exit script
Display("You need to at least supply a name. Please go back.")
email = form["email"].value
email = None
color = form["color"].value
color = None
comment = form["comment"].value
comment = None
Output = "" # our output buffer, empty at first
Output = Output + "Hello, "
if email != None:
Output = Output + "<A HREF="mailto:" + email + "">" +
name + "</A>.<P>"
Output = Output + name + ".<P>"
if color == "swallow":
Output = Output + "You must be a Monty Python fan.<P>"
elif color != None:
Output = Output + "Your favorite color was " + color + "<P>"
Output = Output + "You cheated! You didn't specify a color!<P>"
if comment != None:
Output = Output + "In addition, you said:<BR>" + comment + "<P>"
### Begin actual script
### evaluate CGI request
form = cgi.FieldStorage()
### "key" is a hidden form element with an
### action command such as "process"
key = form["key"].value
key = None
if key == "process":
Notice that we defined the function Display() again. This isn't really necessary; in fact, it's a bad idea to cut-n-paste code from one script to another. A far better solution (which I will show you later) is to put all your functions that are common to multiple scripts in separate file. That way, if you want to improve or debug the function later, you don't have to hunt for each occurrence of it in all of your scripts. However, we can get away with it for this learning exercise.
Note that since Python is a dynamically interpreted language, functions have to be defined before they can be used. That's why the first thing this program does is define its functions.
The DisplayForm() function is very simple and uses the same principle as the Display() function (which it, of course, calls upon to do most of the hard work.) Using these kinds of functions rather than embedding static HTML forms will save you an incredible amount of aggravation. Sometimes, of course, you'll need to generate HTML on-the-fly; we'll talk about that later.
The ProcessForm() function has two main parts. The first extracts values from the <INPUT> fields of the form and stores them in regular variables. This is the section of your program logic where you validate the information in the form to make sure it is kosher by your application's definition.
It is a VERY bad idea to use form data provided by random people on the web without validating it; especially if you're going to use that data to execute a system command or for acting on a database. Naively written CGI scripts, in any language, are a favorite target for malicious system crackers. At the minimum, make sure form information doesn't exceed a certain appropriate length. Passing huge strings to external programs where they aren't expected can cause buffer overflows which can lead to arbitrary code execution by a clever cracker. We will be discussing security in greater detail in the next article. Fortunately, Python makes it easy to write scripts securely.
In our program, we enclose these validation statements in try: and except: blocks because if the value isn't present, it will generate an exception, which could potentially stop your program. In this case, we catch the exception if a given field was left blank (or simply not in the original form.) That variable will be assigned the None value, which has special meaning in Python. (By definition, it is the state of not having a value, similar to NULL in other languages.) A special case is the "name" field; we've decided that that field is absolutely required. If "name" is not given, then the program will use its Display() method to print a helpful message to the user: "Please go back and try again." It then raises the exception SystemExit, which in effect, stops the script then and there.
Once we have validated all of our form input, we can act on it however we choose. In this case, we just want to print a simple message acknowledging receipt of the information. We set up an empty string to use as a buffer for our output, and we begin adding to it. We can conditionally include stuff in this buffer depending on whether or not a value is defined is None. Sharp observers will note that we cheated a bit and put a little actual HTML directly in our code. Again, this is not ideal, but we can get away with it in this learning exercise. The next article in this series will address using objects to avoid the problems of embedding HTML directly in output statements.
The actual main part of the script follows the function definitions, and is very simple. We obtain the form, and conditionally execute one of our two action functions depending on the value of a hidden form element called "key". We know that if this key is present, and it is set to the value "process," we'll want to process the form. Otherwise, we'll just display the form. If you're not sure what I mean by hidden key, go back and look at our form.html template carefully.