shibataのブログ

アルゴリズム・機械学習・統計学などを勉強しています。

Flaskでwebアプリ作成

Flask で web アプリを作成する際のメモ。随時更新
参考:Flask公式サイト

インストール

コマンドラインからpipでインストールする。

$ pip install Flask

Hello world

hello.py というファイルを作成し、下記のコードを書く。

from flask import Flask

app = Flask(__name__)

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

次のコマンドでアプリを起動できる。

$ export FLASK_APP=hello
$ flask run

デバッグモードで起動するには下記のコマンドを実行する。

$ export FLASK_ENV=development
$ flask run

Routing

下記でURLと呼び出すメソッドを関連付けることができる。

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

@app.route('/hello')
def hello():
    return 'Hello, World'

@app.route() で指定するURLの最後に /(スラッシュ)をつけた場合、最後のスラッシュありでもなしでも同じページに遷移する。
例えば下記の場合は、'/projects/' でも '/projects' でも同じ '/projects/' のページに遷移する。
( '/projects' が指定された場合には '/projects/' にリダイレクトされる)

@app.route('/projects/')
def projects():
    return 'The project page'

逆に@app.route() で指定するURLの最後に /(スラッシュ)をつけなかった場合、スラッシュをつけたURLが指定されると404が返される。
例えば下記の場合に '/about/' が指定されると404が返される。

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

HTTP Methods

@app.route()はデフォルトでは GET にしか対応しない。
POSTなど他のHTTPメソッドに対応させるには下記のように 'methods=' のオプションをつける。

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

Static Files

静的ファイルを使用するには、モジュールの隣に staticディレクトリを作成しそこに配置する。
配置したファイルのURLは下記で取得できる。

url_for('static', filename='style.css')

Rendering Templates

Flask では render_template() メソッドを使うことで、Jinja2 のテンプレートエンジンを使用できる。
下記のように 別でhtmlファイルを用意して読み込む。
url_for() を使うことで、静的ファイルのURLを取得することもできる。

  • hello.py
from flask import render_template

@app.route('/hello/')
@app.route('/hello/')
def hello(name=None):
    return render_template('hello.html', name=name)

  • hello.html
{% if name %}
  Hello {{ name }}!
{% else %}
  Hello, World!
  {{ url_for('static', filename='style.css') }}
{% endif %}

The Request Object

フォームの情報を取得するにはrequestオブジェクトが持つform属性を参照する。
例えば request.form['password'] で、passwordという名前のフォームの内容を取得できる。

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

URLからパラメータを受け取るには、request.args.get()を使う。
例えば下記のようにすると、/test_url_param/?key=xxx の場合の xxx が画面に表示される。
存在しないキーが指定された場合には2つ目の引数の内容が返される。

@app.route('/test_url_param/')
def test_url_param():
    return request.args.get('key', '')

File Uploads

アップロードされたファイルを取得するには request オブジェクトの files 属性を参照する。
取得されたファイルはpythonのfileクラスと同じように振る舞う。
save() メソッドでサーバー内に保存できる。

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')

filename属性でユーザーがアップロードしたファイル名を取得できるが、このファイル名をそのまま使用してサーバーに保存するのはセキュリティ上リスクがある。その場合、secure_filename() メソッドを使うことでファイル名をサニタイズできる。

from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['the_file']
        file.save(f"/var/www/uploads/{secure_filename(file.filename)}")

APIs with JSON

下記のようにすればJSON形式で値を返すことができる。

@app.route("/me")
def me_api():
    user = get_current_user()
    return {
        "username": user.username,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image),
    }

Logging

ログを出すには app.logger クラスが使える。
下記のように使用する。

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')