- 追加された行はこの色です。
- 削除された行はこの色です。
- Flaskの基礎 へ行く。
[[Python覚え書き]] >
* Python FlaskでWebアプリ作成 [#se271bb5]
#setlinebreak(on);
以下、PythonのWebフレームワークである Flaskの基礎を記載する。
#contents
-- 関連
--- [[Nginx+uwsgiでFlaskアプリケーション]]
--- [[Nginx+uwsgi+Flaskをdockerで動かす]]
** Flaskのインストール [#ob264ba6]
#html(<div style="padding-left:10px">)
アプリケーション用のディレクトリ 及び 仮想環境の作成
#myterm2(){{
mkdir sample_flask
cd sample_flask
python3 -m venv .venv
source .venv/bin/activate
}}
Flaskインストール
#myterm2(){{
pip install Flask
}}
#html(</div>)
** メイン処理 [#q2e394cf]
#html(<div style="padding-left:10px">)
flask.Flask を import するだけで使用できる。
以下、空のWebアプリケーションを作成して起動する例を記載する。
*** app.py [#j3b34058]
#html(<div style="padding-left:10px">)
#mycode2(){{
# coding: utf-8
"""サンプルアプリケーション"""
from flask import Flask
# アプリケーションのインスタンス作成
# (インスタンス生成時の引数にはアプリケーション名を指定する)
app = Flask(__name__)
# ルーティング
@app.route('/')
def index():
return ''
# 起動(コマンドラインから flask run する場合は不要)
if __name__ == "__main__":
app.run()
}}
#html(</div>)
*** 起動 [#yff770b1]
#html(<div style="padding-left:10px">)
環境変数 FLASK_APP でエントリポイントを指定する( コマンドラインから flask run する場合)
#myterm2(){{
export FLASK_APP=app.py # デフォルトは wsgi.py or app.py
}}
flask run で起動
#myterm2(){{
flask run
# もしくは
python3 app.py
}}
#html(</div>)
#html(</div>)
** ルーティング [#e64ebb20]
#html(<div style="padding-left:10px">)
ルーティングは生成したFlaskインスタンスをデコレータとして利用して記述する
*** 基本形 [#k093fc87]
#html(<div style="padding-left:10px">)
#mycode2(){{
@app.route('/')
def index():
return ''
# 起動(コマンドラインから flask run する場合は不要)
if __name__ == "__main__":
app.run()
}}
#html(</div>)
*** パス(URI)を指定する [#l4686b28]
#html(<div style="padding-left:10px">)
#mycode2(){{
@app.route('/sample')
def sample():
return 'sample!\n'
}}
#html(</div>)
*** メソッドを限定する [#w3b2e71f]
#html(<div style="padding-left:10px">)
#mycode2(){{
@app.route('/books', methods=['POST'])
def book_create():
return 'book create!\n'
}}
#html(</div>)
*** パスパラメータを受け取る [#s1cf4824]
#html(<div style="padding-left:10px">)
#mycode2(){{
@app.route('/books/<id>'):
def book_edit(id):
return f'book_edit : id={id}\n'
}}
#html(</div>)
*** ルーティング情報を確認する [#jcbfc76c]
#html(<div style="padding-left:10px">)
設定済みのルーティング情報は CLI コマンドで確認できる。
#myterm2(){{
flask routes
Endpoint Methods Rule
------------- ------- -----------------------
book_create POST /books
book_edit GET /books/<id>
book_list GET /books
hello GET /hello
if_template GET /if
inctempl GET /inctempl
index GET /
inherit_templ GET /inherittempl
loop_template GET /loop
myscript GET /myscript
sample GET /sample
static GET /static/<path:filename>
webapi GET /webapi
}}
#html(</div>)
#html(</div>)
** リクエストデータの受け取り方法 [#o0b48145]
#html(<div style="padding-left:10px">)
*** パスに入力したデータの取得 [#fe4eb6ba]
#html(<div style="padding-left:10px">)
- @app.route デコレータに <データ形式:項目名> と指定する事で、パスに指定されたパラメータを取得できる。
- データ形式は省略可能
- データ形式が合致しない場合は、404 となる
- データ形式を指定した場合は、そのデータ型で取得される。※省略時は文字列型(str)となる
#html(<div style="display: inline-block;margin-right: 40px;">)
例)
#mycode2(){{
# データ形式を省略する場合
@app.route('/sample1/<id>'):
def sample1(id):
return f'sample1 : id={id}\n'
# データ形式を指定する場合
@app.route('/sample2/<int:id>'):
def sample2(id):
return f'sample2 : id={id}\n'
}}
#html(</div>)
#html(<div style="display: inline-block; vertical-align: top;">)
<指定できるデータ形式>
| 形式 | 説明 |h
| string | 文字列(スラッシュ以外) |
| int | 整数 |
| float | 浮動少数点 |
| path | パス(スラッシュ含む) |
| uuid | UUIDの形式 |
| any | あらゆるパターン |
#html(</div>)
#html(</div>)
*** GETパラメータ(クエリ文字列)の取得 [#ob4d2d2f]
#html(<div style="padding-left:10px">)
flask.request.args から GETパラメータを取得する事ができる。
#mycode2(){{
from flask import request
app = Flask(__name__)
@app.route('/books', methods=['GET'])
def book_list():
limit = int(request.args.get('limit', 10))
return f'books get : limit: {limit}!\n'
}}
結果
#myterm2(){{
curl http://localhost:5000/books?limit=20\&page=1
books get : limit: 20!
}}
#html(</div>)
*** POSTデータの取得 [#rdd0f711]
#html(<div style="padding-left:10px">)
flask.request.form から POSTデータを取得する事ができる。
#mycode2(){{
from flask import request
app = Flask(__name__)
@app.route('/books', methods=['POST'])
def book_create():
var1 = request.form.get('var1')
return f'book create. var1: {var1}!\n'
}}
結果
#myterm2(){{
curl -XPOST --data 'var1=abc' http://localhost:5000/books
book create. var1: abc!
}}
#html(</div>)
*** 補足 [#t29de458]
#html(<div style="padding-left:10px">)
GET用の request.args も POST用の flask.request.form も 実態は ImmutableMultiDict というオブジェクトとなっている。
なので、このオブジェクトの各種メソッドを使用して、パラメータの取得や、存在確認等を行う事ができる。
※上記のサンプルでは get メソッドを取得してデータの取得を行っている。
https://tedboy.github.io/flask/generated/generated/werkzeug.ImmutableMultiDict.html
#html(</div>)
#html(</div>)
** 静的ファイルの配信 [#bb59d317]
#html(<div style="padding-left:10px">)
static というフォルダを作成し、その配下にファイルを配置する事で静的ファイルの配信ができる。
*** フォルダ作成 [#w3a99212]
#html(<div style="padding-left:10px">)
#myterm2(){{
mkdir static
}}
#html(</div>)
*** ファイル配置 [#tfecba8f]
#html(<div style="padding-left:10px">)
static/style.css
#mycode2(){{
h1 {
color: #f00;
}
}}
#html(</div>)
*** CSS読み込みサンプル [#bd913f2b]
#html(<div style="padding-left:10px">)
app.py
#mycode2(){{
@app.route('/')
def index():
return """<!doctype html>
<html lang="ja">
<head>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>"""
}}
#html(</div>)
#html(</div>)
** テンプレートエンジン(jinja2)の利用 [#jf64af67]
#html(<div style="padding-left:10px">)
Flaskのデフォルトのテンプレートエンジンである Jinja2 の利用方法について記載する。
公式サイトにも記載があるが、構文等は Django の影響を強く受けている為、ほとんど Django と同じ。
なので、Djangoでテンプレートを書いた事があればだいたい分かる(ぱっと見は区別がつかないくらい似てる)
http://jinja.pocoo.org/docs/2.10/templates/
*** テンプレート格納用のディレクトリ [#ncd2c0b6]
#html(<div style="padding-left:10px">)
テンプレートの格納先はデフォルトでは、templates 配下となっている為、プロジェクト直下に templates ディレクトリを作成しておく。
#myterm2(){{
mkdir templates
}}
#html(</div>)
*** テンプレートの作成 [#s27b81a3]
#html(<div style="padding-left:10px">)
templates/hello.html
#mycode2(){{
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body>
Hello {{name}}!
</body>
</html>
}}
#html(</div>)
*** 基本的な使用方法 [#e7bf3396]
#html(<div style="padding-left:10px">)
render_template でテンプレートとなるファイルと、埋め込み変数を指定する事でレンダリング結果が取得できる。
例)
#mycode2(){{
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/hello', methods=['GET'])
def hello():
name = request.args.get('name', 'World')
return render_template('hello.html')
}}
#html(</div>)
*** 変数の展開 [#tbcaf245]
#html(<div style="padding-left:10px">)
テンプレートに展開する変数はメイン処理側で render_template の第2引数以降にキーワード引数として指定する。
app.py
#mycode2(){{
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/vars', methods=['GET'])
def vars():
return render_template('vars.html', var1='abc', var2='def')
}}
テンプレート側では、{{変数名}} と記述する事で変数の内容を展開する事ができる。
templates/vars.html
#mycode2(){{
var1: {{var1}}<br/>
var2: {{var2}}<br/>
}}
#html(</div>)
*** 条件分岐 [#r777ed8a]
#html(<div style="padding-left:10px">)
app.py
#mycode2(){{
from flask import Flask, render_template
import time
app = Flask(__name__)
@app.route('/if')
def if_template():
time_sufix = int(str(int(time.time()))[-1])
return render_template('if.html', time_sufix=time_sufix)
}}
templates/if.html
#mycode2(){{
{% if time_sufix % 2 %}
Odd
{% else %}
Even
{% endif %}
}}
#html(</div>)
*** 繰り返し [#e48298e4]
#html(<div style="padding-left:10px">)
#mycode2(){{
# テンプレート内で繰り返し処理
@app.route('/loop')
def loop_template():
items = [
{'index': 0, 'var': 'A'},
{'index': 1, 'var': 'B'},
{'index': 2, 'var': 'C'},
{'index': 3, 'var': 'D'},
{'index': 4, 'var': 'E'}
]
return render_template('loop.html', items=items)
}}
templates.loop.html
#mycode2(){{
<ul>
{% for item in items %}
<li>{{item.index}} : {{item.var}}</li>
{% endfor %}
</ul>
}}
#html(</div>)
*** HTMLタグ等のエスケープ無効化 [#ha6e92a5]
#html(<div style="padding-left:10px">)
Jinja2 では XSS対応としてデフォルトで HTMLタグ等がエスケープされるようになっているが、無効化する事もできる。
app.py
#mycode2(){{
@app.route('/myscript')
def myscript():
return render_template('myscript.html', myscript='<script>alert("test!")</script>')
}}
templates/myscript.html
#mycode2(){{
{% autoescape false %}
{{ myscript }}
{% endautoescape %}
}}
#html(</div>)
*** テンプレートを継承する [#idd1e237]
#html(<div style="padding-left:10px">)
テンプレートを継承して必要な部分のみ上書く事も可能。
http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance
以下、公式サイトのサンプルを、ほぼそのまま記載。
base.html
#mycode2(){{
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright Sample.
{% endblock %}
</div>
</body>
</html>
}}
child.html
#mycode2(){{
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}
}}
#html(</div>)
*** 他のテンプレートを include する [#t8f178af]
#html(<div style="padding-left:10px">)
他のテンプレートを include する事も可能。
http://jinja.pocoo.org/docs/2.10/templates/#include
app.py
#mycode2(){{
@app.route('/inctempl')
def inctempl():
return render_template('inctempl.html', vars=['a', 'b', 'c'])
}}
templates/inctempl.html
#mycode2(){{
{% include "./part_vars.html" %}
}}
templates/part_vars.html
#mycode2(){{
<ul>
{% for var in vars %}
<li>{{var}}</li>
{% endfor %}
</ul>
}}
#html(</div>)
*** テンプレート構文の表示 [#k7ca63b6]
#html(<div style="padding-left:10px">)
raw を使用する事でテンプレートの構文をそのまま表示する事ができる。
#mycode2(){{
# テンプレート内で繰り返し処理
@app.route('/loop')
def loop_template():
items = [
{'index': 0, 'var': 'A'},
{'index': 1, 'var': 'B'},
{'index': 2, 'var': 'C'},
{'index': 3, 'var': 'D'},
{'index': 4, 'var': 'E'}
]
return render_template('loop.html', items=items)
}}
templates.loop.html
#mycode2(){{
<ul>
{% raw %}
{% for item in items %}
<li>{{item.index}} : {{item.var}}</li>
{% endfor %}
{% endraw %}
</ul>
}}
#html(</div>)
*** コメント [#f57fbc8d]
#html(<div style="padding-left:10px">)
templates/comment.html
#mycode2(){{
{# これはコメントです #}
}}
#html(</div>)
*** その他 [#g8b12a91]
#html(<div style="padding-left:10px">)
他にも filter や macro など、様々な機能がある。
http://jinja.pocoo.org/docs/2.10/templates/
#html(</div>)
#html(</div>)
** その他 [#cfe23e81]
#html(<div style="padding-left:10px">)
*** ステータスコードやContent-Typeを指定する [#h02e834c]
#html(<div style="padding-left:10px">)
#mycode2(){{
@app.route('/webapi')
def webapi():
return json.dumps({'var1': 'abc', 'var2': 'def'}), 200, {'Content-Type': 'application/json; charset=utf-8'}
}}
#html(</div>)
#html(</div>)
** サンプルアプリケーション [#e2eb28b2]
#html(<div style="padding-left:10px">)
#TODO(SQLAlchemy を使用した簡単なCRUDアプリケーションの例)
#html(</div>)