If your script is throwing an exception, the (badly formatted) traceback
will appear in the error log.

Can I see CGI script tracebacks without munging through log files?

The cgitb module catches errors for you and tries
to generate nicely formatted HTML output from the traceback.
The problem is that if your script generated HTTP headers
(e.g., Content-Type: text/xml) first
and then throws an exception, the output from cgitb
is treated as your type instead of HTML. To avoid this problem,
you should buffer output from your script as a string, and only
send the string if the script completes successfully.

Can I use external Python packages in my CGI scripts?

The easiest way is to put the Python packages/modules in
the cgi-bin directory next to the CGI scripts.
If you are installing using
pip,
use the -t option to specify where the package
should be installed. For example, to install the social-auth
package in my cgi-bin directory, use:

Note that you need to repeat this for each team member as well as the
team production site since the installed package is only available to
the script in the same directory.

Why does my CGI script work on the server but fail
when accessed from the web?

CGI scripts have different permissions depending on how they
are invoked. When run from the shell on the server, a script
is running as the invoking user, e.g., conrad;
when run as a CGI script, the same script is running as user
apache. This difference in permission is often the
cause of unexpected failures, such as
inability to open sqlite3 databases.

Why does my CGI script work when I run it manually but
fail when accessed from my web page?

CGI scripts have different permissions depending on how they
are invoked. When run from the shell on the server, a script
is running as the invoking user, e.g., conrad;
when run as a CGI script, the same script is running as user
apache. This difference in permission is often the
cause of unexpected failures, such as
inability to open sqlite3 databases.

sqlite3 wants write permission to the directory
containing the database file when inserting or updating rows.
Even if the database file itself is writable, your code can
still get the dreaded sqlite3.OperationalError:
unable to open database file error if the directory
is not writable.

This is a particularly subtle error if your script works when
run on the server but fails when accessed from a web page because
the same script is being run by two different users: user who logged
in to the server and user apache.

For BMI219, we created a data directory that is owned
by user apache but writable by group bmi219. If your
databases are created in there and made generally writable, then
apache should be able to update your database.

Making databases generally writable is not secure. However,
this is the path of least resistance for a short course.
If you want to be more secure, you can write a CGI script
to create the database and invoke it via a web page; this
would make apache the owner of the database file and
therefore updatable via the web without having to be made
generally writable.

Why is my web page being reloaded even when I'm using AJAX?

A common cause of this problem is using a button's
onclick event handler to initiate an AJAX
request. If the type attribute for an
HTML button is not specified, it defaults to submit.
If the onclick handler returns false,
then the form is not submitted. If it returns
true, then the form is submitted and the
entire web page is replaced by the response to the form
submsstion; any AJAX request initiated by the replaced
page is silently discarded.

The workarounds are:

Set the button type attribute to button.

Make sure the button onclick handler returns
false. In specifying the Javascript event handler
code, be sure to either return the value from
whatever function is being called, or always
return false after calling the function.

Use a different input tag to initiate AJAX requests,
e.g., onchange event handler for a
text tag.