## 2 cgi モジュールを使う

先頭には "`import cgi`" と書いてください。"`from cgi import *`"と書いてはなりません -- このモジュールでは、以前のバージョンとの 互換性を持たせるため、内部で呼び出す名前を多数定義しており、それらを ユーザの名前空間に存在させる必要はないからです。

新たにスクリプトを書く際には、以下の一行を付加するかどうか検討してください:

    import cgitb; cgitb.enable()

これによって、特別な例外処理が有効にされ、エラーが発生した際にブラウザ 上に詳細なレポートを出力するようになります。ユーザにスクリプトの内部を 見せたくないのなら、以下のようにしてレポートをファイルに保存できます:

    import cgitb; cgitb.enable(display=0, logdir="/tmp")

スクリプトを開発する際には、この機能はとても役に立ちます。 `cgitb` が生成する報告はバグを追跡するためにかかる 時間を大幅に減らせるような情報を提供してくれます。スクリプトを テストし終わり、正確に動作することを確認したら、いつでも `cgitb` の行を削除できます。

入力されたフォームデータを取得するには、 `FieldStorage` クラス を使うのが最良の方法です。このモジュールで定義されている他のクラスの ほとんどは以前のバージョンとの互換性のためのものです。 インスタンス生成は引数なしで必ず 1 度だけ行います。これにより、 標準入力または環境変数からフォームの内容を読み出します (どちらから読み出すかは、複数の環境変数の値が CGI 標準に従って どう設定されているかで決まります)。インスタンスが標準入力を 使うかもしれないので、インスタンス生成を行うのは一度だけにしなければ なりません。

`FieldStorage` のインスタンスは Python の辞書のようにインデクス を使って参照でき、標準の辞書に対するメソッド `has_key()` と `keys()` をサポートしています。組み込みの関数 `len()` もサポートしています。空の文字列を含むフォームのフィールドは 無視され、辞書には入りません; そういった値を保持するには、 `FieldStorage` のインスタンスを生成する時にオプションの keep_blank_values キーワード引数を true に設定してください。

例えば、以下のコード (Content-Type: ヘッダと空行は すでに出力された後とします) は `name` および `addr` フィールドが両方とも空の文字列に設定されていないか調べます:

    form = cgi.FieldStorage()
    if not (form.has_key("name") and form.has_key("addr")):
        print "<H1>Error</H1>"
        print "Please fill in the name and addr fields."
        return
    print "<p>name:", form["name"].value
    print "<p>addr:", form["addr"].value
    ...further form processing here...

ここで、"`form[key]`" で参照される各フィールドは それ自体が `FieldStorage` (または `MiniFieldStorage`。 フォームのエンコードによって変わります) のインスタンスです。 インスタンスの属性 `value` の内容は対応するフィールドの値で、 文字列になります。 `getvalue()` メソッドはこの文字列値を直接返します。 `getvalue()` の 2 つめの引数にオプションの値を与えると、 リクエストされたキーが存在しない場合に返すデフォルトの値になります。

入力されたフォームデータに同じ名前のフィールドが二つ以上あれば、 "`form[key]`" で得られるオブジェクトは `FieldStorage` や `MiniFieldStorage` のインスタンスではなく、そうしたインスタンスの リストになります。この場合、"`form.getvalue(key)`" も同様に、 文字列からなるリストを返します。 もしこうした状況が起きうると思うなら (HTML のフォームに同じ名前をもったフィールドが複数含まれているのなら) 、 組み込み関数 `isinstance()` を使って、返された値が単一のインスタンスかインスタンスのリストかどうか 調べてください。例えば、以下のコードは任意の数のユーザ名フィールドを 結合し、コンマで分割された文字列にします:

    value = form.getvalue("username", "")
    if isinstance(value, list):
        # Multiple username fields specified
        usernames = ",".join(value)
    else:
        # Single or no username field specified
        usernames = value

フィールドがアップロードされたファイルを表している場合、`value` 属性や `getvalue()` メソッドを使ってフィールドの値にアクセス すると、ファイルの内容を全て文字列としてメモリ上に読み込んでしまいます。 これは望ましくない機能かもしれません。アップロードされたファイルが あるかどうかは `filename` 属性および `file` 属性の いずれかで調べられます。その後、以下のようにして`file` 属性から 落ち着いてデータを読み出せます:

    fileitem = form["userfile"]
    if fileitem.file:
        # It's an uploaded file; count lines
        linecount = 0
        while 1:
            line = fileitem.file.readline()
            if not line: break
            linecount = linecount + 1

現在ドラフトとなっているファイルアップロードの標準仕様では、一つの フィールドから (再帰的な multipart/* エンコーディングを 使って) 複数のファイルがアップロードされる可能性を受け入れています。 この場合、アイテムは辞書形式の `FieldStorage` アイテムと なります。複数ファイルかどうかは `type` 属性が multipart/form-data (または multipart/* に マッチする他の MIME 型) になっているかどうかを調べれば判別できます。 この場合、トップレベルのフォームオブジェクトと同様にして再帰的に 個別処理できます。

フォームが 「古い」 形式で入力された場合 (クエリ文字列または 単一のapplication/x-www-form-urlencoded データで入力 された場合)、データ要素の実体は `MiniFieldStorage` クラスの インスタンスになります。この場合、`list` 、`file` 、および `filename` 属性は常に `None` になります。