Flaskの構成とかライブラリメモ
はじめに
Flaskでいくつかアプリを作るのでメンバーに共有するために、使うライブラリとその説明とかをメモ。 ソースコードはGithubで公開しています。
Flask-SQLAlchemy
モデルの定義
from . import db from werkzeug.security import generate_password_hash, check_password_hash class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128), index=True) password_hash = db.Column(db.String(128)) @property def password(self): raise AttributeError('password is not a readable attribute') @password.setter def password(self, password): self.password_hash = generate_password_hash(password) def verify_password(self, password): return check_password_hash(self.password_hash, password) def __repr__(self): return '<User %r>' % self.name @property def serialize(self): return { 'id': self.id, 'name': self.name, }
状態を表すものにはpropertyデコレータをつける
上の例だとserializeで返すものはUserの別表現みたいなもので、このオブジェクトの状態を表しているため、メソッド(手続き)として定義するのは直感的じゃないです。propertyデコレータをつけてオブジェクトが持つ値のようにみせます。
値の種類が多いカラムには
index=True
をつけるMySQLのインデックスは基本B-Treeインデックスというインデックスを使用しているため、簡単に言うとそのカラムの値の種類が多い(=カーディナリティが高いともいう)場合、インデックスを張っておくと効率的にデータを取得できます。主キーにはインデックスが張られているので不要。
__repr__
属性を定義すると動作確認・デバッグの際に役立ちますserializeはユーザの一覧をJSONで返したい時とかに利用する
from flask import jsonify @app.route('/users') def get_users(): return jsonify(users=[user.serialize for user in User.query.all()])
- 認証にはwerkzeugを使用している。下記のように使えます。
>>> from .models import User >>> u = User() >>> u.password='cat' >>> u.password Traceback (most recent call last): : AttributeError: password is not a readable attribute >>> u.password_hash 'pbkdf2:sha1:1000$pjsh7cVO$33e723c78dfe39b49ade0c7b5db8f034bdd6d31e' >>> u.verify_password('cat') True >>> u.verify_password('cats') False
クエリのプロファイリング
Flask-SQLAlchemyには get_debug_queries
という遅いクエリがないか調べる機能があります。
configに SLOW_DB_QUERY_TIME
を定義しておいて、下のようにすれば遅いクエリをロギングできます。
from flask.ext.sqlalchemy import get_debug_queries @app.after_request def after_request(response): for query in get_debug_queries(): if query.duration >= app.config['SLOW_DB_QUERY_TIME']: app.logger.warning( 'Slow query: %s\nParameters: %s\nDuration: %fs\nContext: %s\n' % (query.statement, query.parameters, query.duration, query.context)) return response
Flask-Script
djangoのmanage.pyみたいなものが作れます。ここでは下記のページに載っている werkzeug
を使ったプロファイリングをFlask-Scriptから簡単に実行できるようにしてみましょう。
# manage.py from flask.ext.script import Manager from app import app, db manager = Manager(app) manager.add_command('db', MigrateCommand) @manager.command def profile(length=25): """Start the application under the code profiler.""" from titl import ProfilerMiddleware app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[length]) app.run() if __name__ == '__main__': manager.run()
上のようにmanage.command
デコレータをつけると、python manage.py profile
とすれば実行できます。
ちなみにrunserver
と shell
はデフォルトで定義されていて最初から使えます。
$ python manage.py runserver -p 8000 $ python manage.py shell
Flask-Migrate
Alembicというマイグレーションツールのラッパーです。Flask-Scriptを利用していて、先ほどのFlask-Scriptのファイルに下記のコードを追加することで、Djangoのように簡単にマイグレーションファイルが生成出来ます。
# manage.py : manager.add_command('db', MigrateCommand)
$ python manage.py db init # 初期化 (./migrations/以下に雛形を生成) $ python manage.py db migrate # マイグレーションファイルの生成 $ python manage.py db upgrade # マイグレート
Flask-Profiler (2015/11/04 追記)
試してないけど、ちょっと気になる。
Flask-DebugToolBar
Flask-DebugToolbar — Flask-DebugToolbar 0.10.0 documentation
Django Debug ToolbarのFlask版。 試してないけど、ちょっと気になる。
Flask-Admin
Flask-Admin — flask-admin 1.3.0 documentation
Django Adminみたいに使える。authenticationとかも簡単に追加できる。 多少カスタマイズをした方がいい。Django Adminよりカスタマイズは簡単そう。
デコレータ
ライブラリとかではないですが、PythonとかFlaskを勉強し始めた時に混乱したのでメモ。 下記のデコレータを利用するとユーザがログイン済みかどうかチェックしてログイン画面にredirectさせたり出来る。
# http://flask.pocoo.org/docs/patterns/viewdecorators/ from functools import wraps from flask import g, request, redirect, url_for def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if g.user is None: return redirect(url_for('login', next=request.url)) return f(*args, **kwargs) return decorated_function
デコレータは一見ややこしいですが、下の記事の解説が分かりやすかったです。
ちなみに functools.wraps
デコレータをつける理由については下の記事が分かりやすいと思います。
HATEOAS APIの実装例
これもライブラリではないですがHATEOAS(Hypermedia As The Engine Of Application State)という概念があります。 APIの返すデータの中に次に行う行動、取得するデータ等のURIをリンクとして含めることで、そのデータを見れば次にどのエンドポイントにアクセスすればよいかが分かるような設計です。Flaskだったら以下のようにすると良いと思います。JSONのAPIを作るときには参考にして下さい。
from flask import Blueprint, jsonify, current_app, url_for from ..models import User, Task api = Blueprint('api', __name__) @api.route('/') def index(): # 次にどのエンドポイントにアクセスすればよいか、URIを渡す return jsonify( users=url_for('.users'), tasks=url_for('.tasks'), ) @api.route('/users') def users(): """ユーザ一覧""" users = User.query.all() return jsonify(Users=[u.serialize for u in users]) @api.route('/task') def tasks(): """タスク一覧""" tasks = Task.query.all() return jsonify(Tasks=[t.serialize for t in tasks])
$ curl http://127.0.0.1:5000/api/v1/ { "users": "/api/v1/users" "tasks": "/api/v1/tasks" }
Flask資料
Flaskについて調べてきた中で見つけた資料とかをメモ
Flask Web Development: Developing Web Applications with Python
- 作者: Miguel Grinberg
- 出版社/メーカー: O'Reilly Media
- 発売日: 2014/04/28
- メディア: Kindle版
- この商品を含むブログを見る
ぼくはこの本で勉強しました。かなりおすすめです。この本の著者はブログにも別のチュートリアルを公開してるのでそっちを読んでもいいかもしれません。
他にはrunnableとかQiitaのFlaskタグ探すといいと思います。
- Newest Flask code - Runnable
- Flaskに関する62件の投稿 - Qiita
- Pythonで学ぶwebアプリケーションの作り方by Flask — study flask 1 ドキュメント
- Web - Flaskで大きいアプリケーションを作るときのTips - Qiita
- atsuoishimoto/pyconjp_2012 · GitHub
- PythonでWebアプリ開発入門のススメ Flask/MongoDB - mizchi log
参考にしたコード
おわりに
これからFlaskのアプリ作りながらもっといい方法とかあればこの記事も編集していきます。