Flaskの基本

2.Flaskの基本構成をつくる

前の記事のおさらいと本記事でやること

前の記事では、Flaskに組み込む予定の、Twitter APIからユーザーのタイムライン情報を取得しCSVとして出力するプログラムを作成しました。

この記事では、そのプログラムを組み込む前に、Flaskの基本構成となるプログラムを作成し、Flaskの入門的な使い方を解説します。

Flaskの入門

基本のディレクトリ構成

FlaskはWebアプリやWebサイトを作るためのフレームワークなので、複数のファイルを用意する必要があります。コード的には一つの.pyファイルのみでも動作しますが、実用的にはメインのプログラムのほかに以下のようなディレクトリ構成が基本となります。

.
├── main.py(メインのプログラム)
├── templates
│   ├── index.html
│   └── example.html
├── static
│   └── style.css
│   └── example.js      
└── example.py

「templates」フォルダにはHTMLファイルを配置します。ほとんどのWebアプリはWebサイトでもあるので、HTMLを書く必要があります。

「static」フォルダにはCSSファイルやJavaScriptファイルを配置します。

フォルダの名前は、原則として「templates」や「static」という風に決まっており、別の名前のフォルダ内のHTMLファイルやCSSファイルなどを利用したい場合は、メインのプログラムで別に指定をしなければなりません。

なお、今回解説するプログラムでは、外部CSSや外部JavaScriptは利用しないので、

.
├── main.py
├── templates
│   └── index.html  
└── fetchtweets.py(前の記事で作成したプログラム)

という、三つのファイルのみのほぼ最小の構成となっています。

Hello, Worldを表示する最初のFlaskアプリ

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello, World!"

if __name__ == '__main__':
    app.run()

これが「Hello, World!」という文字をブラウザ上に表示する最小のFlaskアプリです。

1行目で、Flaskのimportを行います。上記のコードは最小構成なので、flaskからFlaskという基本のクラスのみをimportしていますが、他にメソッド等を使う場合は、

from flask import Flask, render_template, requests, ...

というように、利用するものを追記していきます。

pip install flask

などで、 忘れずにインストールもしておきましょう。

3行目では、Flaskというクラスのインスタンスを作成し、変数appに代入しています。この行の意味は、クラスについて理解していないとよくわからないと思われるので、まだ入門者でクラスについて理解できていない方は最初に書く決まり文句だと思っておけばいいでしょう。

ここで代入した変数appに、後ろのコードでapp.route、app.runなどFlaskのメソッドをつけ足して、Flaskアプリを作っていくことになります。

5行目ではルーティングを行っています。ルーティングというのはURLとプログラム上の関数処理を結びつけることです。

上記のコードでは、@app.route()の引数である'/'、つまりドメインのトップページにアクセスしたときに、6行目の関数hello_world()を実行するという意味になっています。ここの関数名は自由につけることができます。

その関数内の7行目では、returnによりHello, World!という文字列を出力しています。実用的には、上記の例のように文字列データを出力するということはあまりなく、templatesフォルダに用意したHTMLを表示する(render_templateというメソッドを使用)か、何かファイルをダウンロードさせたりJSONデータを表示させるというコードになることが多いでしょう。

ルーティングは、

@app.route('/') 
def index():
    return 'index page'

@app.route('/hogehoge') 
def hogehoge():
    return 'hogehoge page'

のように複数の@app.route()とそれに続く関数を書くことで、各URLに接続したときの場合分けを処理できます。

@app.route('/<language>') 
def dynamic(language):
    return 'Hello ' + language

のように書くとURLへのアクセスに動的に対応することもできます。例えば、上記のコードだと/PythonというURLにアクセスすると、「Hello Python」という文字がブラウザ上に出力されます。

9~10行目はrun()メソッドによりFlaskアプリケーションを動作させるコードで、決まり文句だと思っておけばいいでしょう。

app.run()の引数はここでは何も指定されていませんが、サーバー上で動作させるときはホスト名やポート番号を引数として指定する必要があります。

ローカルでテストする場合は、上記のコードのままで実行すると、コンソール上に

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

のようなメッセージが表示され、表示されたURL(http://127.0.0.1:5000/)にブラウザでアクセスするだけで、Webアプリの確認・デバッグができるようになっています。

なお、デバッグ時には、

app.run(debug=True)

と、引数にdebug=Trueと指定するとデバッグモードになり、コードを変更したときに自動的にリロードしてくれたり、不具合が発生したときにFlask内蔵のデバッガーが立ち上がるので便利です。

PythonのプログラムをWebアプリとして公開するコードの基本部分を作る

それでは、前の記事で作ったプログラムを組み込むためのFlaskのコードを実際に書いてみましょう。

index.htmlを用意する

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>twitterとflaskの連携アプリ</title>
</head>
<body>
    <h1>twitterからツイート・お気に入り数・リツイート数を取得するWebアプリ</h1>
    <p>検索したいユーザーのスクリーンネームを入力してください。</p>
        <form action="/" method="POST">
        <input name="screen_name"/>
            <input type="submit" value="送信する"/>
        </form>
</body>
</html>

CSS等は用意せず、 formタグとinputタグにより検索フォームを設置しているだけのシンプルなページにしています。

フォームの処理はHTMLだけでは出来ず、サーバーサイドの言語(Python、PHP、Rubyなど)で扱う必要がありますが、処理では11行目のinputタグ内のname属性が使われます。上記のコードの例だとname属性はscreen_nameであるため、送信ボタンを押すとPythonのプログラムが「キー:screen_name」、「値:検索欄の入力値」のデータを受け取ることになります。

10行目のformタグ内のaction属性では、送信ボタンがクリックされたときのデータの送信場所を指定します。上記のコードの例だと、"/"、つまりトップページ(今回の例では送信元と同じページ)にデータを送信することになります。別のページに遷移させたいときは、別のパスを指定してください。

action属性をaction=""と空欄にすると、同じページ内でフォームの処理が行われます。

今回の例はトップページのみのサイトなので、action="/"と書いても、action=""と書いても結果は変わりませんが、ページ遷移をさせずにそのままのページで処理を行いたい場合はaction=""と空欄にするのが楽かもしれません。

また、同じformタグ内のmethod属性では、送信ボタンがクリックされたときのデータの送信方法を指定します。POSTまたはGETを指定することになりますが、上記のコードの例だとPOSTを指定しています。

GET通信はデータをURLに付加して送信するのに対し、POST通信はデータをリクエストボディに含めて送信します。

GET通信にすると、例えば

http://example.com/form?screen_name=twitterapi

のように?マーク以降のURLに送信されたデータが含まれるので、ブックマークなどに有用でサイト内の検索ページに主に利用されます。しかし、URLにデータを含めるという仕様のため、送信できるデータ量が少ないのが欠点です。

そのため、フォームの値を送信するときはPOST通信が主に利用されます。

HTMLでフォームを作るときは、formタグのaction属性とmethod属性、inputタグのname属性を指定する必要がある点に注意しましょう。

フォームから送信されたデータを受け取るFlaskのプログラム

main.py
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == "GET":
        return render_template("index.html")

    else:
        screen_name = request.form["screen_name"]
        return screen_name

if __name__ == '__main__':
    app.run()

先ほど用意したindex.htmlを表示し、フォームからデータを受け取るプログラムです。

まず、1行目で今回使うメソッドであるrender_template、requestをimportします。

5行目でルーティングを行います。Hello, worldのプログラムと違い、ここでは引数としてmethodに通信方法を指定しています。これは先ほどのindex.htmlで解説したようにPOST通信を行うためです。

6行目以降のトップページでの処理では、if文によってGETのとき(=今回は通常通りURLにアクセスした場合)と、POSTのとき(=今回はフォームから値が送信された場合)で場合分けを行っています。

if request.method == "GET"節はGETのとき、else節はPOSTのときのブロックです(["GET", "POST"]のうちGETではない方、つまりPOST)。

GET通信が行われたときの処理である8行目では、HTMLを表示するメソッドであるrender_template()を使っています。引数に先ほど用意したindex.htmlを指定しているので、先ほどのHTMLが表示されることになります。

POST通信が行われたときの処理である11行目では、変数screen_nameにフォームに入力された値を受け取り、代入しています。

request.form["inputタグのname属性"]

でフォームの入力値を受け取ることができます。

GET通信の場合は、

request.args.get("URLのクエリパラメータ")

でデータを受け取ることができます。

POST通信の場合でも、

request.form.get("inputタグのname属性")

と書くことができます。後ろにPythonの組み込み関数であるget()をつけると、指定したname属性が存在しないときにエラーが発生せず、Noneまたは第2引数で指定した値が返されます。

※ 逆に今回の例のmain.pyでは、指定したname属性が存在しないとき、エラーが発生します。

受け取ったデータによって動的にHTMLを生成する

次に、フォームから受け取ったデータに応じて、動的にHTMLを表示してみましょう。まずはmain.pyを修正します。

main.py
from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == "GET":
        return render_template("index.html")

    else:
        screen_name = request.form["screen_name"]

        if len(screen_name) == 0:
            return render_template("index.html", error="検索ユーザー名が未入力です。")

        return render_template("index.html", screen_name=screen_name)

if __name__ == '__main__':
    app.run()

13~14行目で、入力されたスクリーンネームが空欄、つまり文字数が0だったときの処理を書いています。

14行目でrender_template()によりHTMLを表示するのは同様ですが、上記の例のように第2引数を指定することにより、「キー:error」、「値:検索ユーザー名が未入力です。」のデータも合わせて送ることができます。

空欄ではなかった場合は、16行目でrender_template()により14行目と同様に動的にHTMLを表示させます。

次にrender_template()で送られたデータをindex.htmlで表示させるための修正を行いましょう。

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>twitterとflaskの連携アプリテスト</title>
    <style type="text/css">
    .red {color:red;}
    </style>
</head>
<body>
    <h1>twitterからツイート・お気に入り数・リツイート数を取得するWebアプリ</h1>
    <p>検索したいユーザーのスクリーンネームを入力してください。</p>
        <form action="" method="POST">
        <input name="screen_name"/>
            <input type="submit" value="送信する"/>
        </form>
    {% if error %}
    <p class="red">{{error}}</p>
    {% endif %}
    {% if screen_name %}
    <p>スクリーンネーム:<b>{{screen_name}}</b></p>
    {% endif %}
</body>
</html>

6~8行目でCSSの設定を追記しています。

そして、17~22行目でif文で囲むことにより、errorというパラメータが送られた場合、screen_nameというパラメータが送られた場合、とそれぞれ動的にHTMLを生成するコードを追記しています。

{% if error %}
{% endif %}

{{screen_name}}

のような表記はJinja2と呼ばれるテンプレートエンジンの記法です。FlaskはJinja2に依存しているため、Jinja2の記法を学習すると開発がスムーズになるでしょう。

実際にブラウザで試してみると、

何かフォームに入力されたとき

何もフォームに入力されなかったとき

というように表示され、動的にHTMLが生成されていることがわかります。

タイトルとURLをコピーしました