はじめに
Webアプリケーションを簡単に作ることができるPython製のWebアプリケーションフレームワークのFlaskの使い方を簡単に見ていくチュートリアルです。
目次
準備編
Flaskについて
まずはFlaskについて知らなければ何も始まりません。
FlaskはWebアプリケーションフレームワークなので、Webアプリケーションを作ることができます。サイトはこちらです。
PythonのWebアプリケーションフレームワークはDjangoが有名ですが、比較的大規模向けで、シングルページのWebアプリケーションや、DBを使わないようなWebアプリケーションには対しては少し冗長な気がします。
それに対してFlaskは小規模な開発に対して学習コストが低く、簡易的なWebアプリケーションであれば、すぐに動作します。なので僕はよくFlaskを使います。
インストール
Flaskはpip経由でインストールできます。
$ pip install flask
簡単ですね。
この記事で使うファイル
ファイル内容は全て記事内に書くので、記事をコピペしていっても動作はするようにします。なお、それぞれのコードの1行目にパスとファイル名を書こうと思います。
基本設定
Webサーバ起動
とりあえず動かしてみて、どのような記述をし、どのように起動をするのか確認してみましょう。
こちらがコードです。
# ./main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello, world!'
if __name__ == '__main__':
app.run()
普通にpython main.pyで実行するとWebサーバが立ち上がり、http://127.0.0.1:5000/にアクセスするとHello, world!と表示されます!
では、それぞれの設定を見ていきましょう。
アドレス変更(外部公開)
先ほどのアドレスがhttp://127.0.0.1:5000/となっていることからわかるように、初期設定ではローカルでサーバが立ち上がるようになっています。
これを他アドレス、例えば外部公開(0.0.0.0)にするにはrunに対して、hostを引数として与えます。次のような感じです。
app.run(host='0.0.0.0')
これで、外部からアクセスできるようになりました。
ポート変更
ポートの変更もrunに引数を与えることで指定できます。例えば次のような感じです。
app.run(port=80)
同時アクセス対応
多数アクセスに対して並列処理を行うには並列処理機能をオンにしなければなりません。こちらもrunに引数を与えます。
app.run(threaded=True)
デバッグモード
編集を加えるたびにいちいちサーバを再起動するのは面倒です。そこで、デバックモードを使うことで、サーバを立てたまま編集が可能になります。こちらもrunに引数を与えます。
app.run(debug=True)
今回の記事内では間違いをすぐ修正できるように、常にこのデバッグモードにしておきますが、適宜自分で変更してください。
HTMLレンダリング
変数埋め込み
ただHello, world!と表示されてもつまらないので、普通のHTMLをレンダリングしてみましょう。
使用するHTMLはこちらです。
<!-- ./templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flaskチュートリアル</title>
</head>
<body>
<p>{{ message }}</p>
</body>
</html>
まず、保存するディレクトリに注意してください。Flaskで使うHTMLはtemplatesに、CSSや画像のリソースはstaticに保存します。こちらは設定で変えることもできますが、それはまたの機会に。
またbodyを見ると見慣れない{{ message }}という囲いがありますね。こちら、Flask側から変数を埋め込むためのプレースホルダです。
では、次にFlaskのファイルです。
# ./main.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
message = 'Hello, world!'
return render_template('index.html', message=message)
if __name__ == '__main__':
app.run(debug=True)
先ほどと比べrender_templateが増えました。この関数により、HTMLにあったプレースホルダを埋めた状態のHTMLを得ることができます。
render_templateは第1引数にHTMLファイル名、プレースホルダにつけた名前のキーワード引数に埋め込む内容を渡します。
この例ではHello, world!と入れた変数を渡しているので、結局Hello, world!と表示されるだけですね笑。
繰り返し表示(for文)
render_templateでは数値や文字列だけでなく、配列を渡し、HTML内でfor文を書くこともできます。
こちらが例です。
<!-- ./templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flaskチュートリアル</title>
</head>
<body>
{% for name in names %}
<p>{{ name }}</p>
{% endfor %}
</body>
</html>
# ./main.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
names = ['Alice', 'Bob', 'Carol']
return render_template('index.html', names=names)
if __name__ == '__main__':
app.run(debug=True)
これで、3人の名前が表示されたはずです。これを使えば様々な活用ができますね!
条件付き表示(if文)
for文が使えるように、HTML内でif文も使えます。
こちらが例です。
<!-- ./templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flaskチュートリアル</title>
</head>
<body>
{% if message %}
<p>{{ message }}</p>
{% else %}
<p>No Message.</p>
{% endif %}
</body>
</html>
# ./main.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
この例ではmessageが存在していないので、elseに回りNo Message.と表示されます。
他のHTMLを埋め込み
例えばHTMLのheadやコンテンツの一部を共有する場合に、他のHTMLに別のHTMLを埋め込むこともできます。
こちらが例です。
<!-- ./templates/layout.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flaskチュートリアル</title>
</head>
<body>
<p>{{ layout_message }}</p>
{% block body %}{% endblock %}
</body>
</html>
<!-- ./templates/index.html -->
{% extends "layout.html" %}
{% block body %}
<p>{{ index_message }}</p>
{% endblock %}
# ./main.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
index_message = 'Here is INDEX.'
layout_message = 'Here is LAYOUT.'
return render_template('index.html',
index_message=index_message,
layout_message=layout_message)
if __name__ == '__main__':
app.run(debug=True)
こちらはlayout.htmlの中にindex.htmlを埋め込んでいます。また変数の埋め込みはどちらのHTMLに対してもできるので、どんどん拡張していけます。
ルーティング
そろそろシングルページは飽きてきましたね。
他のルートも作ってみましょう。実は、先ほどからずっと出てきている@app.route('/')というデコレータを変更することで、ルートを変更できます。
こちらが例です。
# ./main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/1')
def page_1():
return 'page1'
@app.route('/2')
def page_2():
return 'page2'
if __name__ == '__main__':
app.run(debug=True)
これで、http://127.0.0.1:5000/にアクセスするとindex、http://127.0.0.1:5000/1にアクセスするとpage1、http://127.0.0.1:5000/2にアクセスするとpage2と表示されます。
簡単ですね。
URL変数
先ほどのルーティングの例では単純に/1でpage1、/2でpage2と表示されますね。これを100ページまで作りたいとなった時に、全てなんて作ってられません。
そこで、URLから変数を得ることができます。
こちらが例です。
# ./main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/<number>')
def page(number):
return f'page{number}'
if __name__ == '__main__':
app.run(debug=True)
ルートに<変数名>を入れ、関数の引数として指定すると、URLから値を受け取ることができます。
こうすることで、どのような入力が来てもページを生成することができます。
(f-stringsを使用しているのでPythonが3.6未満の人はf'page{number}'を'page'+numbarとかにしてください。)
GET
さあURLで変数を指定できるだけでは微妙です。GETでデータを受け取ってみましょう。
こちらが例です。
<!-- ./templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flaskチュートリアル</title>
</head>
<body>
<form action="/receive" method="GET">
<input type="text" name="text">
<button type="submit">送信</button>
</form>
</body>
</html>
# ./main.py
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/receive')
def receive():
return request.args.get('text')
if __name__ == '__main__':
app.run(debug=True)
送信を押したらそのまま表示される何でもない活用方法ですが、GETで受け取った変数はrequest.args.getで名前を指定することで受け散ることができます。
POST
GETをやったなら次はPOSTです。
こちらが例です。
<!-- ./templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Flaskチュートリアル</title>
</head>
<body>
<form action="/receive" method="POST">
<input type="text" name="text">
<button type="submit">送信</button>
</form>
</body>
</html>
# ./main.py
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/receive', methods=['GET', 'POST'])
def receive():
if request.method == 'POST':
return request.form['text']
return 'Not POST.'
if __name__ == '__main__':
app.run(debug=True)
POSTはPOSTであることをルートのところで明示しなければなりません。受け取りはrequest.formに入っています。これは辞書型のような形式になっているの、それぞれの名前から値を引き出せます。
おわりに
1記事にまとめようとしたら、馬鹿みたいに長くなりました笑。
追記するようなことがあれば、どんどん足していくので、もし書いて欲しいことがあればコメントとかください。