Flask の基本的な使いかたについてメモ

Flask メモ

Flask を実際触ってみたメモを残す

Python の情報

Python 自体の情報について Python 3.6.4 を今回使った。

Install Flask

インストールメモ

$ pip install Flask
$ pip show Flask

上記コマンドで Flask をインストールして、インストールされたことを確認する。
2018-09-24 現在 Flask のバージョンは、 1.0.2 が最新であった。

最新にするには、下記コマンドで出来る。

$ pip install -U Flask

Make Script

スクリプトを実際につくってみる

アプリケーション用のディレクトリを作る

$ mkdir flask
$ cd flask

動作確認も含めた最初のスクリプトを作る
ファイル名: app.py

# coding: utf-8

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello_world():
    return 'hello, world!'

ちなみに、 app = Flask(__name__) について。
__name__ はアプリケーションを直接実行したばいい、 __main__ が入る。
インポートした Flask に対し、引数として __main__ をつけて app というインスタンスを作成する。
というのがこの一文のはず。 その後は、作成した app というインスタンスに対して動作を定義していく。

ちなみに、モジュールとして他のアプリケーションから呼ばれて動作するか、スクリプトとして実行されているかで、 __name__ の中身が変わります。
(だから何を気をつければよいかはパッと出てこない。ごめんなさい)

実行する

$ export FLASK_APP=app.py
$ flask run
 * Serving Flask app "app.py"
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Warning 出ているけど、仮想環境を作っていないからだと思われる。(個人的には仮想環境的を作るより、 Vagrant や Cloud 環境を使って試したほうが良いと考えている)

動作確認
ブラウザを開いて、 http://localhost:5000/ にリクエストを投げる。

hello, world!

と出力されたらOK
もしくは、

$ curl -sSv http://localhost:5000/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 13
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:02:55 GMT
< 
* Closing connection 0
hello, world!$ 

のように、コマンドで確認しても良い。
上記の場合、 localhost:5000 に対して GET リクエストを出しているのがわかる。 Responce で HTTP/1.0 200 OK となっていることから、リクエストが正常にレスポンスされていることがわかる。

Closing connection 0 のあとで、

hello, world!

が出力されていることがわかる。 ! の後ろに改行コードを入れていないことから、レスポンス文字列は改行されず、プロンプトの $ が出力されていることがわかる。

Deep dive a bit

ちょっとだけ深く潜ってみる。

app.py

# coding: utf-8

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

@app.route("/")
def hello_world():
    return 'hello, world!'

@app.route("/about")
def about():
    return "<h1>About: </h1>"

@app.route("/hello1/<whom>")
def hello1(whom):
    return "Hello {}\n".format(whom)

@app.route("/hello2/<whom>")
def hello2(whom):
    return render_template("hello.html", whom=whom)

@app.route("/100_plus/<int:n>")
def adder(n):
    return "100+{}={}".format(n, 100+n)

上記スクリプトでは、テンプレートから参照できるようにもしている。
テンプレートもこのタイミングで作ってみる。

$ mkdir templates

テンプレートの html ファイルを作成する。
hello.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Hello</title>
</head>
<body>
  <h1>Using Jinja2 Template engine</h1>
  <h2>Hello {{whom}}<h2>
</body>
</html>

上記テンプレートは、 def hello2(whom): 内で呼ばれる。

動作確認

$ curl -sSv http://localhost:5000/about
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /about HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 16
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:17:30 GMT
< 
* Closing connection 0
<h1>About: </h1>$

ヘッダとして About: 文字列が表示されている

$ curl -sSv http://localhost:5000/hello1/someone
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /hello1/someone HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 14
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:19:13 GMT
< 
Hello someone
* Closing connection 0
$

Hello の後に、 URL の文字列で渡した、 someone が反映されているのがわかる。

$ curl -sSv http://localhost:5000/hello2/somebody
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /hello2/somebody HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 183
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:21:15 GMT
< 
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Hello</title>
</head>
<body>
  <h1>Using Jinja2 Template engine</h1>
  <h2>Hello somebody<h2>
</body>
* Closing connection 0
</html>$

テンプレートを使ってレンダリングしていることがわかる。また、 {{whom}} の内容が、 URL で渡された文字列になっていることがわかる。

  1. URL の文字列を @app.route("/hello2/<whom>)<whom> で受け取る
  2. def hello2(whom):whom に値を渡す
  3. render_template("hello.html", whom=whom) で、テンプレート hello.html を利用し、且つテンプレート内の whom 変数に対し、 whom の変数に入っている値を渡してあげた上で、レンダリングしていることがわかる
$ curl -sSv http://localhost:5000/100_plus/100
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /100_plus/100 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 11
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:28:53 GMT
< 
* Closing connection 0
100+100=200$

100100 という文字列結合ではなく、 200 という計算結果になっていることから、与えられた値が int 型として与えられていることがわかる。

では、文字列を渡してみる。

$ curl -sSv http://localhost:5000/100_plus/foo
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /100_plus/foo HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 404 NOT FOUND
< Content-Type: text/html
< Content-Length: 233
< Server: Werkzeug/0.14.1 Python/3.6.4
< Date: Mon, 24 Sep 2018 08:31:37 GMT
< 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again.</p>
* Closing connection 0

404 エラーになった。
int 型になりうる値以外は例外を出すという処理に対して期待どおりに動くことがわかった。
バリデーションに使えそう。

ちなみにこれらのひととおりの app.py 側のログは以下のように出力された。

127.0.0.1 - - [24/Sep/2018 17:17:03] "GET /about HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:17:17] "GET /about HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:17:30] "GET /about HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:19:13] "GET /hello1/someone HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:21:15] "GET /hello2/somebody HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:28:53] "GET /100_plus/100 HTTP/1.1" 200 -
127.0.0.1 - - [24/Sep/2018 17:31:37] "GET /100_plus/foo HTTP/1.1" 404 -

出来れば日時表記は ISO 規格に合わせたいところである。

以上、やってみたログでした。