21
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flaskの便利な使い方まとめ

Last updated at Posted at 2020-06-02

flaskはpythonでウェブアプリケーションを作るためのライブラリである。ここでは、flaskを使ううえでの基本的な方法をまとめる。

#インストール

pip install flask

#導入

まず最初に、最も簡単な"Hello World"を返すAPIを作る。

app.py
from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    app.run()
  • 関数@app.route(url)でデコレートすると、そのurlにアクセスすると、その関数が実行されるようになる。
python app.py

上で作成したapp.pyを実行すると、ウェブサーバーが起動する。ブラウザでhttp://localhost:5000/にアクセスすると、画面にHello World!が表示されるようになる。

#開発時の設定

app.py
from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    app.run(debug=True,host='localhost',port=5000)
  • mainを上のように変える。
  • debug=Trueを渡すと(デバッグモード)、サーバーが起動している時に、app.pyや後述するhtmlファイルなどを変更した時に、自動的にロードされる。debugの初期値はFalse
  • hostportは自由に決めれる。portの初期値は5000。

#外部からアクセスできるようにする。

app.py
from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    app.run(debug=False,host='0.0.0.0',port=5000)
  • mainを上のように変える。
  • host=localhostの時にはローカルからしかアクセスできない。外部からアクセスできるようにするには、host="0.0.0.0"
  • セキュリティの理由からdebug=Falseにする。

#ブラウザも同時に起動する

app.py
import threading,webbrowser
from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    threading.Timer(1.0, lambda: webbrowser.open('http://localhost:5000') ).start()
    app.run(debug=False)
  • mainでブラウザーを開くコマンドを追加する。
  • app.pyを実行すると同時にブラウザーが立ち上がり、ページが表示される。
  • app.run()debug=Trueを渡すと、コードを変更するたびにmainが再実行されるため、その度にブラウザーが追加で開いてしまう。それを防ぐためdebug=Falseを渡している.

デバッグモード(debug=True)でブラウザーを自動で開くようにしたい時には、main

if __name__ == "__main__":
    app.run(debug=True)

にして、

#!/bin/sh
( sleep 2; open http://localhost:5000/ ) &
python app.py

のようなシェルスクリプトを実行するような方法がある。

#htmlファイルを返す

app.py
from flask import Flask,render_template
app = Flask(__name__)

@app.route('/')
def main():
    return render_template('index.html')

if __name__ == "__main__":
    app.run()
/templates/index.html
<html>
<body>
  Hello World!
</body>
</html>
  • htmlファイルを返す時にはrender_template(file)を用いる。
  • htmlファイル(ここではindex.html)はapp.pyと同じ場所にtemplatesというディレクトリを作り、その中に配置する。
  • http://localhost:5000/にブラウザからアクセスするとindex.htmlがレンダリングされ、この場合は"Hellow World!"が表示される。

#CSS・JavaScriptファイル

  • CSSファイルやJavaScriptファイルはディレクトリ./staticに配置する。画像もここに配置する。
  • htmlファイルからは、それぞれ
<head>
  <script src="/static/script.js"></script>
  <link rel="stylesheet" href="/static/style.css">
</head>

のように呼び出す。

#GETリクエスト

  • ここでは/getのアドレスにGETリクエストを受けて、{"message":"Hello World!"}というjsonオブジェクトを返すapiを考える。
app.py
from flask import Flask,request,jsonify,render_template
app = Flask(__name__)

@app.route('/')
def index_html():
    return render_template('index.html')

@app.route('/get',methods=['GET'])
def get():
    return jsonify({'message':'Hello world!'})

if __name__ == '__main__':
    app.run()
  • デコレータ@app.route(url,methods)でリクエストを受け入れるurlmethodsを指定。
  • jsonify()はオブジェクトを文字列になおし、適切なレスポンスを生成する関数である。
/templates/index.html
<html>
<body>
  <button onclick='get();'>get</button>

  <script>
    function get(){
      fetch('/get').then(
        response => response.json()
      ).then(
        json => { document.body.innerHTML += JSON.stringify(json); }
      ).catch(
        e => console.error(e)
      );
    }
  </script>
</body>
</html>
  • ボタンを押すと、返ってくるJSONを文字列として表示される。

fetchが成功したい時に何か処理を行いたい時には、次のようにすれば良い。

fetch('/get').then(
  response => (
    if (response.ok){
        //成功時に行ないたい処理
    } else {
        throw new Error()
    }
  ).catch(
    e => console.error(e)
  );

#POSTリクエスト

  • ここでは/postのアドレスにPOSTリクエストを受けて、同じJSONをそのまま返すapiを考える。
app.py
from flask import Flask,request,jsonify,render_template
app = Flask(__name__)

@app.route('/')
def index_html():
    return render_template('index.html')

@app.route('/post',methods=['POST'])
def post(): #送られてきたjsonをそのまま返す。
    req = request.json
    return jsonify(req)

if __name__ == '__main__':
    app.run()
  • POSTリクエストで送られてきたJSONはrequest.jsonで得られる。
/templates/index.html
<html>
<body>
<button onclick='post();'>post</button>

<script>
function post(){
  fetch('/post', {
    method: "POST",
    headers: { "Content-Type": "application/json"},
    body: JSON.stringify({"message": "Hello World!!!"})
  }).then(
    response => response.json()
  ).then(
    json => { document.body.innerHTML += JSON.stringify(json); }
  ).catch(
    e => console.error(e)
  );
}
</script>
</body>
</html>
  • ここでは、ボタンを押すと{'message':'Hello World!!!'}のJSONをデータとして持つPOSTリクエストを送る。同じJSONが返ってくるのでそれを表示する。

#画像データを返すAPI

サーバーはデータを受信し、matplotlibでグラフを作成し、その画像データを返すようなAPIを考える。ここでは画像データをdataURLに変換してデータを返す。dataURLは画像データをurlとして表現したものである。

app.py
from flask import Flask,render_template,jsonify,request

import matplotlib
matplotlib.use('agg')
from matplotlib import pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
import io,base64

app = Flask(__name__)

@app.route('/')
def main():
    return render_template('index.html')

@app.route('/image',methods=['POST'])
def image():
    data = request.json
    x = data['x']
    y = data['y']

    #図を作成
    plt.figure(figsize=(3,3),dpi=100)
    plt.plot(x,y,'k*-',markersize=10)

    #図をdataURLに変更
    canvas = FigureCanvasAgg(plt.gcf())
    png_output = io.BytesIO()
    canvas.print_png(png_output)
    img_data = base64.b64encode(png_output.getvalue()).decode('utf-8')
    dataurl = 'data:image/png;base64,'+img_data

    return jsonify({'img_data':dataurl})

if __name__ == "__main__":
    app.run()
  • まず、サーバー上での図の描画になるので、下のコマンドをmatplotlib関連のライブラリのimportの一番最初に行うことに注意する。
import matplotlib
matplotlib.use('agg')
  • image()ではmatplotlibでデータからグラフを作成し、それをdataURLに変換し、それを返している。
/templates/index.html
<html>
<body>

<button onclick="image();">img</button>
<div id='img'></div>

<script>

function image(){
  fetch('/image', {
    method: "POST",
    headers: { "Content-Type": "application/json"},
    body: JSON.stringify({"x":[0,1,2,3,4,5], "y":[5,2,4,0,3,8]})
  }).then(
    response => response.json()
  ).then(
    json => { document.getElementById('img').innerHTML = `<img src="${json['img_data']}">`; }
  ).catch(
    e => console.error(e)
  );
}

</script>

</body>
</html>
  • imgボタンをクリックすると、データx=[0,1,2,3,4,5]y=[5,2,4,0,3,8]をサーバーに送る。
  • レスポンスが返ってきたら、dataURLをimgタグのsrcに入れれば画像が表示される。

#クロスオリジンリクエスト(COR):別のドメインからのアクセス

デフォルトでは別のドメインからのアクセスは制限されている。これを許可するためにはflask-corsのパッケージを使う。

pip install flask-cors
app.py
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

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

if __name__ == "__main__":
    app.run()
  • 単にCORSappに適用するだけでOK。

#ユーザー認証(ダイジェスト認証)

認証のためにflask-httpauthのパッケージを用いる。

pip install flask-httpauth
app.py
from flask import Flask
from flask_httpauth import HTTPDigestAuth

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPDigestAuth()

users = {
    'Michael': 'pw_m',
    'Smith': 'pw_s'
}

@auth.get_password
def get_pw(username):
    if username in users:
        return users.get(username)
    return None

@app.route('/')
@auth.login_required
def root():
    return "Hello World!"

if __name__ == "__main__":
    app.run()
  • ユーザー名を与えられた時に、そのユーザーが存在するならば、そのユーザーのパスワードを返し、ユーザーが存在しないならば、Noneを返す関数を作り(get_pw)、それを@auth.get_passwordでデコレートする。
  • ここではユーザは'Michael'と'Smith'二人存在し、パスワードはそれぞれ'pw_m'、'pw_s'であるとする。
  • 認証が必要な関数を@auth.login_requiredでデコレートする。
  • アクセスすると認証画面が最初に表れ、正しいユーザー名・パスワードを入力すると、"Hello World"が表示される。
21
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?