Python

Flask 使ってみた

More than 1 year has passed since last update.

Python で軽量な Web フレームワークで人気のあるらしい Flask を使ってみたメモ
Bottle にしなかったのは Flask が View にロジック書けない実装になっているらしい(参考)から。
ぶっちゃけほぼなんとなく。

Python なのは自分に馴染みがあるから(自分が ruby 弱すぎるというのもある)

インストール

Flask のドキュメント読むと積極的に virtualenv を勧められているし、恐らくそうするのが正しいのだろうが、 そんな色々な開発するほどには個人的にならないと思うので pyenv で十分と判断し、そのままインストールしていく。
(環境試して壊してってしたい人は virtualenv 使ったほうがいいと思います)

やっといたほうが後々便利なのでやっぱりやります。
※ pyenv, pyenv-virtualenv で実施:参考

$ cd /path/to/workingdirectory/
$ mkdir flask_test
$ cd flask_test
$ pyenv virtualenv 3.6.1 flask_test_3.6.1
$ pyenv local flask_test_3.6.1
$ echo .python-version > .gitignore

dependency が Werkzeug (ヴェルクツォイクと読むらしい) と Jinja2 らしいので入れる。

$ pip install Werkzeug
$ pip install Jinja2

終わったので Flask 入れる

$ pip install Flask

終わったらしい

$ pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
click (6.7)
Flask (0.12.2)
itsdangerous (0.24)
Jinja2 (2.9.6)
MarkupSafe (1.0)
pip (9.0.1)
setuptools (36.6.0)
Werkzeug (0.12.2)
wheel (0.30.0)

いいと思う(たぶん)

試す

web server の起動

参考:https://qiita.com/zaburo/items/5091041a5afb2a7dffc8

写経

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

@app.route('/')
def hello():
    return "Hello Flask!"

@app.route('/good')
def good():
    return "Good page."

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

動かす

$ python ./hello.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: hogehoge

動いてるっぽいのでアクセス

http://localhost:5000/
スクリーンショット 2017-10-25 13.01.04.png

http://localhost:5000/good
スクリーンショット 2017-10-25 13.01.14.png

表示された。こりゃあ楽でいいわ

テンプレートエンジンの利用

写経

共通テンプレート(外組)

layout.html
<!DOCTYPE html>
<html>
    <head>
        <title>{{ title }}</title>
        <body>
            {% block content %}
            <!-- main content -->
            {% end block %} ←後述するが謝り
        </body>
    </head>
</html>

個別コンテンツ(内組)

hello.html
{% extends "layout.html" %}
{% block content %}
<h3>Hello</h3>
hello, {{ name }}.
{% end block %} ←後述略

呼び出し側プログラムの改修

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

@app.route('/')
def hello():
    #return "Hello Flask!"
    return render_template('hello.html', title="this is Title!", name="kokomi")

@app.route('/good')
def good():
    return "Good page."

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

あまり関係ないが、hello.py に変更を加えて保存した瞬間 reload された

 * Detected change in '/path/to/wrokingdirectory/hello.py', reloading
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: hogehoge

再度アクセス
スクリーンショット 2017-10-25 13.29.06.png

あべしっ!
どうやらテンプレートの場所が悪いらしい

before
$ tree .
.
├── hello.html
├── hello.py
└── layout.html

0 directories, 3 files
after
$ tree .
.
├── hello.py
└── templates
    ├── hello.html
    └── layout.html

1 directory, 3 files

気を取り直して
スクリーンショット 2017-10-25 13.34.17.png

あべしっ!(Syntax Error とは恥ずかしい)

hello.html(before)
{% extends "layout.html" %}
{% block content %}
<h3>Hello</h3>
hello, {{ name }}.
{% end block %} ←ここ
hello.html(after)
{% extends "layout.html" %}
{% block content %}
<h3>Hello</h3>
hello, {{ name }}.
{% endblock %} ←こう

※ layout.html も同じ
※ 写経はちゃんとスペースにも気をつけてやりましょう

今度こそ!
http://localhost:5000/
スクリーンショット 2017-10-25 13.38.12.png

やったぜ :tada:

DB の利用

を、しようと思ったけどこの辺になってくると動かす先でどう構築するかって話になってくるのでやめる

heroku で動かす

せっかくなので heroku にデプロイできるようにしてみたい。
参考:https://qiita.com/kounoike/items/6fc31fe051e5d688f136

heroku 用に色々ファイルを記述(多分)
gunicorn は多分 heroku で動かすのに必要なはず…

$ pip install gunicorn
$ pip freeze > requirements.txt
$ echo python-3.6.1 > runtime.txt
$ echo web: gunicorn app:app --log-file=- > Procfile ←後述するが誤り

git repository

$ git init
$ git add .gitignore
$ git add .
$ git commit -m "initial commit"
$ heroku login
$ heroku apps:create
$ git push heroku master

アクセスしてみる
スクリーンショット 2017-10-25 16.59.09.png

┐(´д`)┌

Procfile コピペしたせいだった

web: gunicorn app:app --log-file=-
web: gunicorn hello:app --log-file=-

スクリーンショット 2017-10-25 17.06.55.png
無事デプロイ完了

DB を使う(データの永続化)

せっかくなのでこのまま参考にして MongoHQ を使う

$ heroku addons:add mongohq
Creating mongohq on ⬢ abababababa... !
 ▸    Please verify your account to install this add-on plan (please enter a credit card) For
 ▸    more information, see https://devcenter.heroku.com/categories/billing Verify now at
 ▸    https://heroku.com/verify

(´・ω・`)

クレカ登録し直してから(無料範囲のはずなので料金はかからないはず)再実施
普通に課金されました。(気づいてすぐ止めたので2$くらい)
Pricing 見ると最低プランで 18$/month なので、今は無料やってないのかな?

$ heroku addons:add mongohq
$ pip install pymongo
$ pip freeze > requirements.txt

app 修正(汚いけどまとめるためこうしてます)
※ 後述するが数カ所誤りあり

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

# add for MongoHQ test
import os
from urlparse import urlparse
from pymongo import Connection

MONGO_URL = os.environ.get('MONGOHQ_URL')

if MONGO_URL:
    con = Connection(MONGO_URL)
    db = con[urlparse(MONGO_URL).path[1:]]
else:
    con = Connection('localhost', 27017)
    db = con['flask_test']

@app.route('/count/<name>')
def count(name=''):
    if name=='':
        name = 'NoName'

    count_obj = db.count.find_one({'name': name})
    if not count_obj:
        count_obj = {'name': name, 'count': 1}
    else:
        count_obj['count'] += 1
    db.count.save(count_obj)

    return render_template('count.html', title="Count Page", count=count_obj)
# /add

@app.route('/')
def hello():
    return render_template('hello.html', title="this is Title!", name="kokomi")

@app.route('/good')
def good():
    return "Good page."

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

view 追加
※ 英語力の苦情は受け付けません

count.html
{% extends "layout.html" %}
{% block content %}
<h3>Count</h3>
hello, {{ count.name }}.
you visit this page {{ count.count }} times.
{% endblock %}

こんな感じで

$ tree .
.
├── Procfile
├── hello.py
├── requirements.txt
├── runtime.txt
└── templates
    ├── count.html
    ├── hello.html
    └── layout.html


再度デプロイ

$ git add .
$ git commit -m "add counter"
$ git push heroku master

アクセスしてみる
スクリーンショット 2017-10-25 17.43.58.png

┐(´д`)┌
慌てず騒がず heroku logs してログ表示

$ heroku logs
(前略)
2017-10-25T08:39:55.456744+00:00 app[web.1]: ModuleNotFoundError: No module named 'urlparse'
(後略)

アッハイ

$ pip install urlparse
Collecting urlparse
  Could not find a version that satisfies the requirement urlparse (from versions: )
No matching distribution found for urlparse

ないんかーい
仕方ないので調べて修正

urlparseurllib.parse に変更して push→まだ駄目

慌てず騒がず(略

(前略)
2017-10-25T08:51:53.122808+00:00 app[web.1]: ImportError: cannot import name 'Connection'
(後略)

Oh...
どうやらバージョンが違う模様

ConnectionMongoClient に変更
最終的に↓な感じ

hello.py(before抜粋)
import os
from urlparse import urlparse
from pymongo import Connection

MONGO_URL = os.environ.get('MONGOHQ_URL')

if MONGO_URL:
    con = Connection(MONGO_URL)
    db = con[urlparse(MONGO_URL).path[1:]]
else:
    con = Connection('localhost', 27017)
    db = con['flask_test']

@app.route('/count/<name>')
def count(name=''):
    if name=='':
        name = 'NoName'

    count_obj = db.count.find_one({'name': name})
    if not count_obj:
        count_obj = {'name': name, 'count': 1}
    else:
        count_obj['count'] += 1
    db.count.save(count_obj)

    return render_template('count.html', title="Count Page", count=count_obj)

hello.py(after抜粋)
import os
from urllib.parse import urlparse
from pymongo import MongoClient

MONGO_URL = os.environ.get('MONGOHQ_URL')

if MONGO_URL:
    con = MongoClient(MONGO_URL)
    db = con[urlparse(MONGO_URL).path[1:]]
else:
    con = MongoClient('localhost', 27017)
    db = con['flask_test']

@app.route('/count/<name>')
def count(name=''):
    if name=='':
        name = 'NoName'

    count_obj = db.count.find_one({'name': name})
    if not count_obj:
        count_obj = {'name': name, 'count': 1}
    else:
        count_obj['count'] += 1
    db.count.save(count_obj)

    return render_template('count.html', title="Count Page", count=count_obj)

アクセス!
スクリーンショット 2017-10-25 18.01.05.png

やったー! :tada:

感想

  • マイクロフレームワークというだけあって 1 ファイルで書けて簡単でとっつきやすい
  • 構造が単純だからどこに何があるのかすぐ分かって楽
  • 普通に Python でそのまま書けるので今までローカルで動かしてた自作スクリプトとか Web に移行しやすそうで良い