フォームによるクライアント・サーバ間データ通信

ここでは、クライアント(Webページを見ているユーザ)から与えられた入力を、pythonの変数として獲得し、出力に反映させる方法について述べます。
まず、以下のコードを見てください。

from bottle import route, run, get, post, request


# クライアントからサーバへデータを与えるためのページ
@route('/')
def befor_func():
    t = '<!DOCTYPE html><html>'
    t = t + '<head>'
    t = t + '<title> PythonHTML </title>'
    t = t + '<meta http-equiv="content-type" charset="utf-8">'
    t = t + '</head>'
    t = t + '<body>'

    ############# フォーム開始 #############
    t = t + '<form action="/output" method="post">'

    # テキスト入力
    t = t + '<p>'
    t = t + '<h3>テキスト入力:</h3>'
    t = t + '<input type="text" name="input_text">'
    t = t + '</p>'

    # チェックボタン
    t = t + '<p>'
    t = t + '<h3>チェックボタン:</h3>'
    t = t + '<input type="checkbox" name="input_check" value="1"> りんご'
    t = t + '<input type="checkbox" name="input_check" value="2" checked> みかん'
    t = t + '</p>'

    # ラジオボタン
    t = t + '<p>'
    t = t + '<h3>ラジオボタン:</h3>'
    t = t + '<input type="radio" name="input_radio" value="1"> 美味しい'
    t = t + '<input type="radio" name="input_radio" value="2" checked> まずい'
    t = t + '</p>'

    # スライダー
    t = t + '<p>'
    t = t + '<h3>スライダー:</h3>'
    t = t + '<input type="range" name ="input_range" value="1" min="1" max="10" step="0.1">'
    t = t + '</p>'

    # 数字
    t = t + '<p>'
    t = t + '<h3>数字:</h3>'
    t = t + '<input type="number" name ="input_num" value="1" min="1" max="10" step="0.1">'
    t = t + '</p>'

    # カラー
    t = t + '<p>'
    t = t + '<h3>カラー:</h3>'
    t = t + '<input type="color" name ="input_col">'
    t = t + '</p>'

    # 送信ボタン
    t = t + '<input value="push" type="submit" />'
    t = t + '</form>'
    ############# フォーム終了 #############

    t = t + '</body>'
    t = t + '</html>'

    return t


# クライアントから受け取ったデータを受け取るページ
@post('/output')
def aft_func():

    # フォームの入力を取得
    output_text = request.forms.input_text
    output_check = request.forms.getlist("input_check") # 複数選択の場合があるため
    output_radio = request.forms.input_radio
    output_range = request.forms.input_range
    output_num = request.forms.input_num
    output_col = request.forms.input_col

    t = '<!DOCTYPE html><html>'
    t = t + '<head>'
    t = t + '<title> PythonHTML </title>'
    t = t + '<meta http-equiv="content-type" charset="utf-8">'
    t = t + '</head>'
    t = t + '<body>'
    t = t + "<p>あなたの回答:</p>"
    t = t + "テキスト: " + output_text + "<br>"
    t = t + "チェックボックス: " + str(output_check) + "<br>"
    t = t + "ラジオボタン: " + output_radio + "<br>"
    t = t + "スライダー: " + output_range + "<br>"
    t = t + "数字: " + output_num + "<br>"
    t = t + "カラー: " + output_col + "<br>"
    t = t + '</body>'
    t = t + '</html>'

    return t

# サーバの起動(一番下に書くこと)
run(reloader=True, port=9999, debug=True)

フォームの定義(14行目〜58行目):

フォームと呼ばれる、ユーザの入力を行う領域を定義しています。ボタンを押すと15行目で定義されているように、「/output」へと飛ぶようになっています。「/output」に相当するページは、67行目以降に定義されています。飛ぶ前のurlは「http://xxx.xxx.xxx.xxx:9999/」であり、飛んだあとのurlは「http://xxx.xxx.xxx.xxx:9999/output」となります。xxx.xxx.xxx.xxxはサーバを起動した時のターミナルに出力される数字列となります。9999はポート番号であり、97行目で定義されています。ポート番号の後ろの「/」「/output」という部分は、5行目、67行目にて定義しています。

テキスト入力欄の作成(17行目〜21行目):

inputタグのtypeにtextと入れると、テキスト入力欄が作成できます。nameで指定した文字列(input_text)に対し、入力された文字列が貯蔵されます。

チェックボックスの作成(23行目〜28行目):

inputタグのtypeにcheckboxと入れると、チェックボックスが作成できます。チェックボックスは複数選択でるユーザインタフェースとなります。nameで指定した文字列(input_check)に対し、入力された文字列が貯蔵されます。27行目に、checkedという文字が見えます。これは初期からチェックしておくという意味があります。りんごにチェックが入れば、valueにあるように、input_checkが1を保持し、みかんにチェックが入ればinput_checkが2を保持します。りんごとみかんにチェックが入っているときは、input_checkが配列となり[1,2]を保持するようになります。

ラジオボタンの作成(30行目〜35行目):

チェックボックスとほぼ同じです。ただチェックボックスとは異なり、複数選択ができません。したがって、input_radioは配列とはなりません。

スライダーの作成(37行目〜41行目):

inputタグのtypeにrangeを指定すると、スライダーを用意できます。minとmaxはスライダーの最小と最大です。stepは分割幅を表します。valueは初期値です。

数字の作成(43行目〜47行目):

inputタグのtypeにnumberを指定すると、数字の選択欄が出ます。min, max, stepで最小、最大、分割幅を指定することができます。valueは初期値です。

カラーの作成(49行目〜53行目):

inputタグのtypeにcolorを指定すると、色を選択できるようになります。デザイン系の研究や感性評価などで使いそうですね。

入力データの取得(70行目〜76行目):

name領域に指定された変数(input_colorなど)に対し、request.forms.を付与すると、htmlのデータがpythonの変数として取得できます。チェックボックスのみ複数選択可能ですから、配列として取得する必要性があるので、少し異なる書き方をしています。

htmlの作成(78行目〜92行目):

最後に、pythonの文字列でhtmlを生成します。returnにてこの文字列を返すと、ページが表示されます。

理解できたら、ターミナルで「python3 sample.py」とうち、実行結果を確認してみてください。ターミナルに出るxxx.xxx.xxx.xxxx:9999をブラウザに打ち込めばokです。

for文などで、html側の変数をpythonに連続的に渡したい場合は、以下のようにします。

a = []
for i in range(0, 10):
   a.append(request.forms.get("input_text"+str(i))


GetとPostについて

GetとPostは、HTMLでのデータの受け渡しを意味します。これには以下の違いがあります。

  • Get: URLに対して送信するデータをそのまま記載する送受信方法
  • Post: URLに送信するデータを記載しない送受信方法

これをみてわかるように、基本的にはPostにより送信した方が安全です。上のプログラムでは、postメソッドによりデータの受け渡しをしています。これを確認するため、以下の行を確認してみてください。

postメソッドによるデータの受け渡し:

  • 15行目: データ送信側で、formのmethodがpostとなっている。
  • 67行目: データ受信側のページが、post指定されている。
  • 71〜76行目: formsで、データを受け取っている。

15行目より、送信側のformによりpost指定されており、67行目における受け取り側のページでもpost指定されていますね。post指定されているデータの送信は、formsを用いて受け取りを行います。


Getメソッドを使う場合

以下は、getメソッドによるデータの送受信です。上のコードに対して、以下の変更点があるのみです。

getメソッドによるデータの受け渡し:

  • 15行目: データ送信側で、formのmethodがgetとなっている。
  • 67行目: データ受信側のページが、get指定されている。
  • 71〜76行目: queryで、データを受け取っている。

送信側と受信側がどちらもgetになっています。要するに、メソッドをきちんと合わせればokです。なお、getメソッドによるデータをpythonで受け取るには、queryを使用します。これを実行し、URLにユーザの選択肢が表示されていることを確認してみてください。

from bottle import route, run, get, post, request


# クライアントからサーバへデータを与えるためのページ
@route('/')
def befor_func():
    t = '<!DOCTYPE html><html>'
    t = t + '<head>'
    t = t + '<title> PythonHTML </title>'
    t = t + '<meta http-equiv="content-type" charset="utf-8">'
    t = t + '</head>'
    t = t + '<body>'

    ############# フォーム開始 #############
    t = t + '<form action="/output" method="get">'

    # テキスト入力
    t = t + '<p>'
    t = t + '<h3>テキスト入力:</h3>'
    t = t + '<input type="text" name="input_text">'
    t = t + '</p>'

    # チェックボタン
    t = t + '<p>'
    t = t + '<h3>チェックボタン:</h3>'
    t = t + '<input type="checkbox" name="input_check" value="1"> りんご'
    t = t + '<input type="checkbox" name="input_check" value="2" checked> みかん'
    t = t + '</p>'

    # ラジオボタン
    t = t + '<p>'
    t = t + '<h3>ラジオボタン:</h3>'
    t = t + '<input type="radio" name="input_radio" value="1"> 美味しい'
    t = t + '<input type="radio" name="input_radio" value="2" checked> まずい'
    t = t + '</p>'

    # スライダー
    t = t + '<p>'
    t = t + '<h3>スライダー:</h3>'
    t = t + '<input type="range" name ="input_range" value="1" min="1" max="10" step="0.1">'
    t = t + '</p>'

    # 数字
    t = t + '<p>'
    t = t + '<h3>数字:</h3>'
    t = t + '<input type="number" name ="input_num" value="1" min="1" max="10" step="0.1">'
    t = t + '</p>'

    # カラー
    t = t + '<p>'
    t = t + '<h3>カラー:</h3>'
    t = t + '<input type="color" name ="input_col">'
    t = t + '</p>'

    # 送信ボタン
    t = t + '<input value="push" type="submit" />'
    t = t + '</form>'
    ############# フォーム終了 #############

    t = t + '</body>'
    t = t + '</html>'

    return t


# クライアントから受け取ったデータを受け取るページ
@get('/output')
def aft_func():

    # フォームの入力を取得
    output_text = request.query.input_text
    output_check = request.query.getlist("input_check") # 複数選択の場合があるため
    output_radio = request.query.input_radio
    output_range = request.query.input_range
    output_num = request.query.input_num
    output_col = request.query.input_col

    t = '<!DOCTYPE html><html>'
    t = t + '<head>'
    t = t + '<title> PythonHTML </title>'
    t = t + '<meta http-equiv="content-type" charset="utf-8">'
    t = t + '</head>'
    t = t + '<body>'
    t = t + "<p>あなたの回答:</p>"
    t = t + "テキスト: " + output_text + "<br>"
    t = t + "チェックボックス: " + str(output_check) + "<br>"
    t = t + "ラジオボタン: " + output_radio + "<br>"
    t = t + "スライダー: " + output_range + "<br>"
    t = t + "数字: " + output_num + "<br>"
    t = t + "カラー: " + output_col + "<br>"
    t = t + '</body>'
    t = t + '</html>'

    return t

# サーバの起動(一番下に書くこと)
run(reloader=True, port=9999, debug=True)