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
写経
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
動いてるっぽいのでアクセス
表示された。こりゃあ楽でいいわ
テンプレートエンジンの利用
写経
共通テンプレート(外組)
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<body>
{% block content %}
<!-- main content -->
{% end block %} ←後述するが謝り
</body>
</head>
</html>
個別コンテンツ(内組)
{% extends "layout.html" %}
{% block content %}
<h3>Hello</h3>
hello, {{ name }}.
{% end block %} ←後述略
呼び出し側プログラムの改修
#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
あべしっ!
どうやらテンプレートの場所が悪いらしい
$ tree .
.
├── hello.html
├── hello.py
└── layout.html
0 directories, 3 files
$ tree .
.
├── hello.py
└── templates
├── hello.html
└── layout.html
1 directory, 3 files
あべしっ!(Syntax Error とは恥ずかしい)
{% extends "layout.html" %}
{% block content %}
<h3>Hello</h3>
hello, {{ name }}.
{% end block %} ←ここ
{% extends "layout.html" %}
{% block content %}
<h3>Hello</h3>
hello, {{ name }}.
{% endblock %} ←こう
※ layout.html も同じ
※ 写経はちゃんとスペースにも気をつけてやりましょう
今度こそ!
http://localhost:5000/
やったぜ
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
┐(´д`)┌
Procfile コピペしたせいだった
web: gunicorn app:app --log-file=-
web: gunicorn hello:app --log-file=-
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 修正(汚いけどまとめるためこうしてます)
※ 後述するが数カ所誤りあり
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 追加
※ 英語力の苦情は受け付けません
{% 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
┐(´д`)┌
慌てず騒がず 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
ないんかーい
仕方ないので調べて修正
urlparse
→ urllib.parse
に変更して push→まだ駄目
慌てず騒がず(略
(前略)
2017-10-25T08:51:53.122808+00:00 app[web.1]: ImportError: cannot import name 'Connection'
(後略)
Oh...
どうやらバージョンが違う模様
Connection
→ MongoClient
に変更
最終的に↓な感じ
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)
↓
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)
やったー!
感想
- マイクロフレームワークというだけあって 1 ファイルで書けて簡単でとっつきやすい
- 構造が単純だからどこに何があるのかすぐ分かって楽
- 普通に Python でそのまま書けるので今までローカルで動かしてた自作スクリプトとか Web に移行しやすそうで良い