ウェブアプリケーションフレームワーク Flask を使ってみる

  • 285
    いいね
  • 0
    コメント

Ruby は柔軟なプログラミング言語であり Sinatra のような手軽で軽量なウェブアプリケーションフレームワークがあります。いままで分析用の言語として主に Python で統計やデータの可視化をおこなってきましたが、もちろん Python にも多種多様なウェブアプリケーションフレームワークがあります。

数値計算などを Python でおこなう仕組みをウェブシステムとして提供したい場合、わざわざ別の言語を利用するよりウェブの部分も同じ言語で作ってしまったほうが一貫性があります。

そこで今回は Flask という Python の小規模なフレームワークを利用し、ごく簡単なウェブアプリケーションを作ってみます。

Flask には日本語訳された親切なユーザーガイドがあります。
http://a2c.bitbucket.org/flask/

とても丁寧に書かれているので、基本的にはこのドキュメントを読みながら手を動かしていけば誰でもウェブシステムを作ることができるでしょう。

ただこれでも分量がありますのでプログラミングが不得手な方だとこれを読み進めるだけでもまだ厳しいかもしれません。時間が無い方にとっても億劫でしょう。そこでさらにその簡易な入門として、テキストデータを送信してその結果を返ってくるというだけのアプリを手っ取り早く作ってみたいと思います。

必要なものは Flask がインストールされた Python 環境だけです。環境構築については他の記事を読めば良いので省略します。

Python を使う最大とも言えるメリットはやはり pandasNumPy に代表される数値計算やデータ分析系のライブラリの充実ぶりです。そこでサンプルアプリケーションでも NumPy を利用することにします。

目的のプログラムを実装する

まずはウェブを通して実現したい目的の部分を作ってしまいましょう。ビジネスロジックとも言います。

sample.py
# Flask などの必要なライブラリをインポートする
from flask import Flask, render_template, request, redirect, url_for
import numpy as np

# 自身の名称を app という名前でインスタンス化する
app = Flask(__name__)

# メッセージをランダムに表示するメソッド
def picked_up():
    messages = [
        "こんにちは、あなたの名前を入力してください",
        "やあ!お名前は何ですか?",
        "あなたの名前を教えてね"
    ]
    # NumPy の random.choice で配列からランダムに取り出し
    return np.random.choice(messages)

# ここからウェブアプリケーション用のルーティングを記述
# index にアクセスしたときの処理
@app.route('/')
def index():
    title = "ようこそ"
    message = picked_up()
    # index.html をレンダリングする
    return render_template('index.html',
                           message=message, title=title)

# /post にアクセスしたときの処理
@app.route('/post', methods=['GET', 'POST'])
def post():
    title = "こんにちは"
    if request.method == 'POST':
        # リクエストフォームから「名前」を取得して
        name = request.form['name']
        # index.html をレンダリングする
        return render_template('index.html',
                               name=name, title=title)
    else:
        # エラーなどでリダイレクトしたい場合はこんな感じで
        return redirect(url_for('index'))

if __name__ == '__main__':
    app.debug = True # デバッグモード有効化
    app.run(host='0.0.0.0') # どこからでもアクセス可能に

トップ画面で名前の入力を促し、送信されたらそれを画面に表示する。それだけのプログラムです。ルーティングのところを除けば、ほぼ普通の Python プログラムであることがおわかりになるかと思います。

テンプレートをレンダリングするには render_template() メソッドを利用します。このとき渡したいオブジェクトを引数として付けるとビューで利用できるようになります。

画面を実装する

画面のことはビューとも言います。ユーザーガイドにもある通り Flask は Jinja2 というテンプレートエンジンを備えています。

Jinja2
http://jinja.pocoo.org/docs/dev/

基本的には普通の HTML ですが、特殊な記法を間にはさむことによって Python から渡されたオブジェクトを表示したり、条件分岐やループなどの処理をすることができます。

レイアウトを用意する

すべての画面に CSS や JavaScript を読み込むためのヘッダーなどを書くのは面倒ですし無駄です。そこでウェブアプリケーションでは共通の部分のみを layout.html のようにレイアウト用の HTML をひとつ用意してしまうのが普通です。

今回は CSS フレームワークである Bootstrap を利用します。 Flask では CSS や JavaScript のようなファイルは /static ディレクトリに置くようになっています。そこで公式サイトからダウンロードした Bootstrap の .zip を展開して static の下に配備します。

また templates というディレクトリを用意してこの下に HTML を置きます。

layout.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {% if title %}
      <title>{{ title }}</title>
    {% else %}
      <title>Bootstrap 101 Template</title>
    {% endif %}
    <!-- Bootstrap -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    {% block content %}{% endblock %}
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="/static/js/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="/static/js/bootstrap.min.js"></script>
  </body>
</html>

基本的に Bootstrap の Basic Template をそのまま書きました。

{% ... %} で囲まれた部分が Jinja2 です。この例では、タイトルがインスタンスとして存在すればそれを表示する、 content という名前のブロックをはさむ、ということをしています。

個別のページをつくる

今回は index.html 1 ページしかないので簡単です。

最初の {% extends "layout.html" %} という部分で layout.html を拡張しています。これでレイアウト以外の部分だけを書けば良くなったわけです。また {% block content %}{% endblock %} で囲うことにより content という名前のブロックという扱いになります。これでレイアウトの content ブロックの部分にレンダリングされるというわけです。

index.html
{% extends "layout.html" %}
{% block content %}
  <!-- Form
  ================================================== -->
<div class="form">
  <div class="container">
    <div class="row">
      <div class="col-md-12">
        <p class="lead">
          {% if name %}
            こんにちは {{ name }} さん
          {% else %}
            {{ message }}
          {% endif %}
        </p>
        <form action="/post" method="post" class="form-inline">
          <label for="name">名前</label>
          <input type="text" class="form-control" id="name" name="name" placeholder="Name">
          <button type="submit" class="btn btn-default">送信する</button>
        </form>
      </div>
    </div>
  </div>
</div>
{% endblock %}

name インスタンスがあれば名前を表示、なければメッセージを表示します。

アプリケーションを起動する

たったこれでアプリケーションは完成です。あとは python app.py のようにすると localhost の 5000 番ポートにウェブアプリケーションが起動します。

1.png

無事トップページが表示されました。メッセージは NumPy によってランダムに選ばれて表示されます。ここに名前を入力して送信ボタンを押すと /post に送信されます。

2.png

リクエストフォームから名前を取得し index.html をレンダリングをします。これによって入力した名前が画面に表示されるわけです。

まとめ

Python でもウェブアプリケーションを手軽に作ることができることがおわかりになったかと思います。このように分析の部分だけでなくウェブなど他の部分まで同じ言語で記述することができるのは R 言語との差別化になると言っても良いかもしれません。

簡単な応答機能をそなえた計算システムなどを用意したいとき Python のプログラムを拡張してそのままウェブアプリケーションとして実装してしまえば何かと便利です。

なお、送信されたデータをデータベースに蓄積したり、データをデータベースから読み出したりするのも SQLAlchemy などを組み合わせることで簡単にできます。これだけでも実用的なアプリケーションが作れそうだという感覚がつかめてきたのではないでしょうか。

今回の記事のソースコードはこちらに公開してあります。