## 11.2.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` になります。