このサイトについて

みんなのPython Webアプリ編 - フォームの処理

みんなのPython Webアプリ編 - フォームの処理

フォームの処理

URLにクエリを埋め込み、Webサーバ上で動くプログラムに値を渡す方法は簡易で便利ですが、欠点が多いのも事実です。長い文字列や複雑な値を渡すのには不向きですし、だいいちアプリケーションの利用者にとって扱いやすい方法とは言えません。そこでここでは、「フォーム(Form)」を使ってもう少し気の利いたUIを作ってみましょう。

フォームを使ってWebサーバに値を渡す

先ほど作った13日の金曜日を数えるプログラムでは、クエリとして西暦を渡していました。クエリを書き換えて西暦を指定するのはとても不便です。ここでは、フォームを使って西暦を入力、指定できるようにしてみましょう。つまり、アプリケーションをコントロールするためのUIを作るわけです。

フォームで作るUIでも、西暦を入力するための「入力窓」を設置します。また、他に部品として「ボタン」を設置する必要があります。フォームでは、ボタンを押して初めてリクエストが送信され、処理の結果をレスポンスとして得ることができます。

次のような内容のHTMLファイルを作って、cgi-binのあるフォルダと同じフォルダに設置します。つまり、プログラムを設置したフォルダの「1つ上」にファイルを設置することになります。ファイルを保存するときの文字コードは「UTF-8」を使ってください。

List03 f13form.html

:::html
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"> </head>
<body>
  <form action="/cgi-bin/find13f.py" method="GET"> 13日の金曜日が何日あるかを探します。<br /> 西暦を入力してください :
    <input type="text" name="year" />
    <input type="submit" name="submit" />
  </form>
</body>
</html>

HTMLについて簡単に解説します。<form>タグで囲まれた部分が、ユーザインターフェースの定義になっています。<form>タグには2つの「アトリビュート」があります。1つはactionというアトリビュートで、文字列の形で先ほど作ったプログラムのパスを指定しています。このようにすることで、リクエストを送る先を指定するのです。もう1つはmethodというアトリビュートです。このアトリビュートについては後ほど解説します。

<form>タグの中には、2つの<input>タグがあります。1つは、西暦を入力するための入力窓です。typeというアトリビュートにtextという文字列を指定して、テキストフィールドを表示しています。nameというアトリビュートには、クエリのキーが埋め込まれています。このようにすることで、テキストフィールドに埋め込まれた文字列をクエリの値として送信することができるのです。

もう1つの<input>タグではリクエストを送信するためのボタンを表示して います。typeアトリビュートにsubmitという文字列を指定すると、リクエスト送信用のボタンが表示されます。フォームを表示するHTMLファイルを設置したら、次のURLを開いてください。

:::url
http://127.0.0.1:8000/f13form.html

もちろん、Pythonで作ったWebサーバは起動しておきます。フォームが表示されたでしょうか。西暦に相当する数字を入力して、結果を確かめてみてください。

図02 西暦入力用のフォーム

図02 西暦入力用のフォーム

テキストフィールドに西暦を入力してボタンを押すと、Webサーバにリクエストが送信されます。リクエストを送信する先は13日の金曜日を数えるプログラムです。

ボタンを押したあとWebブラウザのURLを確認してみてください。「/cgi-bin/find13f.py?year=...」のようになっているはずです。つまり、フォームに入力された内容がクエリに変換され、URLに埋め込まれているわけです。プログラムでは、URLに埋め込まれたクエリから情報を受け取り、13日の金曜日を探し出しています。

フォームの動的出力

これまでは、UIとなるフォームをHTMLファイルとして書き出していました。HTMLファイルもWebアプリケーションの出力も、テキストベースのレスポンスであるという意味では同じです。つまり、プログラムを使ってUIを表示する、ということも可能なのです。

HTMLファイルのように、事前に作っておいた静的なファイルをWebサーバからレスポンスとして出力する方法を「静的出力」と呼ぶことがあります。それに対して、プログラムを使ってレスポンスを出力することを「動的出力」と呼ぶことがあります。

静的出力は、HTMLファイルなどをあらかじめ作ってサーバに設置しておけばよいため、とても手軽に行えます。半面、いつも決まった内容しか出力できません。

動的出力は、レスポンスを返すプログラムを作る必要があるため少々面倒です。半面、状況に応じて出力される文字列の内容を変えることができます。Webサーバに表示される内容を、状況に応じて変えることができるわけです。

フォームを作るとき、状況によって表示内容などを変えたいことがあります。たとえば、先ほどの「13日の金曜日」を表示するWebアプリケーションで、西暦をメニュー(<select>タグ)を使って表示するとしましょう。現在や、その付近の西暦について調べたいことが多いでしょうから、以下のようにメニューを表示するのが親切です。

  • 現在を起点に、過去、未来それぞれ10年を表示
  • 現在の西暦を選択状態で表示

「現在の西暦」は、Webアプリケーションをいつ動かすかによって変化します。このような場合は、フォーム・コントロールに相当する文字列を動的に作り、Webブラウザに表示した方がよさそうです。

先ほどのプログラムを改良して、結果だけでなくフォームを表示するようにしてみましょう。「find13f.py」という名前でcgi-binの下にファイルを保存し、WebブラウザでプログラムのURLにアクセスしてみてください。プログラムで作ったフォームが表示されるはずです。

List04 f13form.py

:::python
#!/usr/bin/env python
# coding: utf-8

import cgi
from datetime import datetime
html_body = u"""
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" /> </head>
<body>
  <form method="POST" action="/cgi-bin/find13f.py">
西暦を選んでください: <select name="year">
      %s
    </select>
    <input type="submit" />
  </form>
%s </body>
</html>"""
options=''
content=''
now=datetime.now()
for y in range(now.year-10, now.year+10):
    if y!=now.year:
        select=''
    else:
        select=' selected="selected"'
    options+="<option%s>%d</option>" % (select, y)
form=cgi.FieldStorage()
year_str=form.getvalue('year', '')
if year_str.isdigit():
    year=int(year_str)
    friday13=0
    for month in range(1, 13):
        date=datetime(year, month, 13)
        if date.weekday()==4:
           friday13+=1
           content+=u"%d年%d月13日は金曜日です" % ( year, date.month)
           content+=u"<br />"
    if friday13:
        content+=u"%d年には合計%d個の13日の金曜日があります" % ( year, friday13)
    else:
        content+=u"%d年には13日の金曜日がありません"
print "Content-type: text/html;charset=utf-8¥n"
print (html_body % (options, content)).encode('utf-8')

このプログラムでは、フォーム・コントロールのうちメニューの項目(<option>タグ)に相当する文字列をプログラムで作っています。メニュー項目に相当する文字列はループを使って作っています。ループの中では、項目の西暦が現在の西暦かどうかを判別して、「selected」というアトリビュートを追加しています。

2つのmethod:GETとPOST

先ほどのフォームにあった<form>タグには「method」というアトリビュートがありました。このアトリビュートは、クエリの送信方法を指定するために利用されます。GETという文字列を指定すると、クエリをURLに埋め込んで送信します。

methodアトリビュートには、GETの他にPOSTという文字列も指定できます。先ほどのHTMLの「method="GET"」の部分を「method="POST"」に書き換えて、ブラウザで開いてみてください。西暦を入力してボタンを押すと、今度はURLにクエリが「現れなくなった」はずです。

URLにはクエリがありませんが、プログラムは正しく動いています。プログラムでは、yearというクエリがない場合は「西暦を入力してください」という警告表示をするはずです。警告表示がなく、ちゃんと13日の金曜日を計算しているということは、なんらかの方法でクエリが送信されているということになります。このように、POSTメソッドを使うと、URLの一部を使わずにクエリを送信することができるのです。

GETメソッドとPOSTメソッドの違い

GETメソッドは、特定の性質を持ったデータを取得したいときに利用されます。GETメソッドは、Webアプリケーションに送信するデータがURLとして残ります。このため、リンクにクエリを含んだURLを埋め込む、というような使い方ができます。何度も同じ内容のクエリを送信できるのです。半面、クエリの長さに制限があります。そのため、大きなサイズのデータを送ることができません。

一方、POSTメソッドは、データの新規作成や更新のために利用されます。データのサイズにも制限がないため、大きなデータを送信できます。ただし、クエリのデータはリクエストの背後で送信されます。同じ内容のクエリを再送信するには、フォームなどから同じデータを再度POSTする必要があります。

GETとPOSTの違いや、具体的にどのような方法でクエリが送信されるかについては、後ほど詳しく解説します。

いろいろなフォーム・コントロール

Webアプリケーションではフォームを使ってUIを表示します。フォームを使って表示できるUIにはいくつかの種類があります。先ほどの例で使った「テキストフィールド」と「ボタン」はよく使われるUIの例です。このようなUIの部品をコントロールと呼びます。

Web上にコントロールを表示するために、いくつかのタグが定義されています。コントロールを表示したい位置にタグを記述することで、Webアプリケーションで利用するUIを表示できるのです。

コントロールを表示するタグには、nameというアトリビュートを置くようにします。このアトリビュートに指定された値が、フォームから送信されるクエリのキーとなります。コントロールの種類によっては、さらに追加のアトリビュートを指定できたり、タグの中に入れ子のタグを記述する必要がある場合があります。

ここでは、フォーム・コントロールの中で最もよく利用されるものをいくつか紹介します。フォームに入力された値をWebアプリケーション側でどのように扱うかについてもあわせて解説します。

図03 いろいろなフォーム・コントロール

図03 いろいろなフォーム・コントロール

テキストフィールド

1行のテキストを入力するために利用するコントロールです。nameアトリビュートにクエリのキーを指定します。sizeアトリビュートを指定すると、コントロールの横幅を指定できます。コントロールにデフォルト文字列を表示したいときには、valueアトリビュートに文字列を指定します。

テキストフィールドの例:

:::html
<input type="text" name="first_name" size="20" />

Webアプリケーション側で受け取るテータは文字列のデータになります。日本語のようなマルチバイト文字列がある場合には注意が必要です。Webアプリケーション側では、文字列データは8ビット文字列として受け取ります。Pythonのプログラムでは、クエリをユニコード文字列に変換をするとよいでしょう。

クエリ上の文字列の扱いについては、のちほど重点的に解説します。

サブミットボタン

フォームからリクエストを送信するボタンとして利用するコントロールです。nameアトリビュートにクエリのキーを指定します。valueアトリビュートに指定した文字列はボタンのラベルとして表示されます。

サブミットボタンの例:

:::html
<input type="submit" name="submit" value="送信" />

 同じフォームに複数のサブミットボタンを設置し、name、valueを変えることで、Webアプリケーション側で押されたボタンを判別できるようになります。たとえば、フォームに「add」と「update」というnameを持つ二種類のsubmitボタンを設置するとします。addというnameのボタンを押すと、もう1つのサブミットボタンupdateのクエリは送信されません。Webアプリケーション側では、クエリのキーを調べることによって、どのボタンが押されたかを知ることができるわけです。

リセットボタン

typeアトリビュートを「reset」とすると「リセット」ボタンを設置できます。リセットボタンは、フォームの入力値をクリアするためのボタンです。valueアトリビュートに指定した文字列がボタンのラベルとして表示されます。

リセットボタンの例:

:::html
<input type="reset" name="reset" value="クリア" />

ラジオボタン

複数の項目から1つだけを選択する、というコントロールを作るときにラジオボタンを使います。テキストフィールドなどと同じく<input>タグを使い、typeアトリビュートにradioという文字列を指定します。

ラジオボタンの例:

:::html
<input type="radio" name="gender" value="male">男性 <input type="radio" name="gender" value="female">女性

同じnameを持つラジオボタンのタグがグループとして扱われます。タグにcheckedというアトリビュートを「checked="checked"」のように設置すると、そのコントロールがあらかじめ選択された状態で表示されます。

同じグループのラジオボタンは1つだけ選択することができます。複数の項目から複数選択するようなコントロールが必要なときにはチェックボックスを使います。

Webアプリケーションには、選択されたコントロールのvalueアトリビュートが、nameに対する値として送信されます。受け取るデータ型は文字列です。valueを変えることによって、どのコントロールが選択されているかを知ることができるわけです。

チェックボックス

複数の項目から複数を選択する、というコントロールを作るときにチェックボックスを使います。<input>タグを使い、typeアトリビュートにcheckboxという文字列を指定します。同じnameを持つチェックボックスのタグがグループとして扱われます。複数の項目を選択できる、という点を除いては、ラジオボタンにとても似たコントロールです。コントロールをあらかじめ選択した状態で表示したいときには、ラジオボタンと同様に「checked="checked"」のようなアトリビュートをタグに設置します。

チェックボックスの例:

:::python
<input type="checkbox" name="language" value="Python">Python
<input type="checkbox" name="language" value="Ruby">Ruby

Webアプリケーションには、選択されているコントロールのvalueが、nameに対する値として送信されます。valueを変えることによって、どのコントロールが選択されているかを知ることができるわけです。

項目が複数選択された場合、Webアプリケーション側では「文字列のリスト」としてデータを受け取ります。同じフォーム中に存在する、同じnameアトリビュートを持つコントロールが複数選択された場合には、プログラムではnameに相当するキーの値として文字列のリストを受け取るのです。プログラム側では、あるnameがどのような種類のコントロールから送られてくるのかを、あらかじめ想定して処理をする必要があります。

メニュー

主に複数の項目から1つ選択するために利用するコントロールです。機能としてはラジオボタンと同じですが、コントロールを表示する面積が小さくて済むのが特徴です。半面、すべての選択項目を見るにはメニューをクリックしなければならない、という不便さがあります。複数の選択項目から1つだけ項目を選択する必要があり、選択項目が多い場合に利用するとよいでしょう。

メニューの例:

:::html
<select name="language">
  <option value="python">Python</option>
  <option value="ruby">Ruby</option>
  <option value="perl">Perl</option>
  <option value="php">PHP</option>
</select>

例を見ると、これまでのコントロールとタグの構成が大きく異なっているのが分かると思います。コントロール全体を囲む<select>タグがあり、ここにnameアトリビュートを記述します。メニュー項目の要素は、<select>タグの子供となるように<option>タグを書き指定します。項目名とクエリの値に相当する文字列は、<option>タグの開始タグと終了タグで囲んで指定しています。<option>タグにvalueというアトリビュートを設置すると、項目が選択されていたときに送信されるクエリの文字列を指定できます。selectedというアトリビュートを「selected="selected"」のように設置すると、その項目が選択された状態でコントロールが表示されます。

テキストエリア

複数行にわたる、テキスト入力用のコントロールを表示します。開始タグと終了タグをペアにして記述します。nameアトリビュートでクエリのキーを指定するのは他のコントロールと同じです。また、colsアトリビュートで入力エリアの横幅を、rowsアトリビュートで縦の長さを指定します。

テキストエリアの例:

:::html
<textarea name="body_text" cols="40" rows="20" >文章を入力してください</textarea>

タグで囲まれた部分はデフォルトの文字列としてコントロールの中に表示されます。開始タグと終了タグの間に改行やスペースがあると、文字列としてコントロールの中に表示されます。コントロールの中を空にしておきたい場合には、開始タグと終了タグの間をあけないようにしておきます。

Webアプリケーション側で受け取るデータは文字列となります。Pythonのプログラムでは、必要に応じてユニコード型に変換して処理をします。

2014-09-03 15:00