先日、AppEngine 向けには自分用のツールだけを開発すると宣言しました。
自分用のツールなら Kay みたいなヘビー級のフレームワークを使うまでもないです。なので、Flask を使っています。
ただ、Flask はビューとテンプレートの機能ぐらいしか提供していないので、足りない機能は他のライブラリで補う必要があります。AppEngine なら、モデルは db モジュール、フォームは WTForms を使えばいいですね。
でも、それだけではまだ不便。そこで AppEngine ユーティリティを集めた Flask 拡張を作ってみました。
#!/usr/bin/env python # -*- coding: utf-8 -*- """ flask_appengine ------------------ Adds basic App Engine support for Flask. :copyright: (c) 2011 by tnakaura. :license: BSD, see LICENSE for more details. """ from functools import wraps from google.appengine.api import users from werkzeug.exceptions import ( NotFound, Unauthorized, Forbidden, ) from flask import ( request, redirect, ) class AppEngine(object): def __init__(self, app=None): if app is not None: self.app = app self.init_app(self.app) else: self.app = None def init_app(self, app): self.app = app self.app.context_processor(self._inject_users) def _inject_users(self): return dict( create_login_url=self.create_login_url, create_logout_url=self.create_logout_url, get_current_user=users.get_current_user, is_current_user_admin=users.is_current_user_admin) def create_login_url(self, url=None): """Computes the login URL for redirection. """ if url is None: url = request.url return users.create_login_url(url) def create_logout_url(self, url=None): """Computes the logout URL for this request and specified destination URL, for both federated login App and Google Accounts App. """ if url is None: url = request.url return users.create_logout_url(url) def login_required(func): """A decorator to require that a user be logged in to access a view. """ @wraps(func) def _login_required(*args, **kwargs): user = users.get_current_user() if user is None: if request.is_xhr: raise Unauthorized() else: return redirect(users.create_login_url(request.url)) return func(*args, **kwargs) return _login_required def admin_required(func): """A decorator to require that a user be admin to access a view. """ @wraps(func) def _admin_required(*args, **kwargs): if not users.is_current_user_admin(): user = users.get_current_user() if user: raise Forbidden() elif request.is_xhr: raise Unauthorized() else: return redirect(users.create_login_url(request.url)) return func(*args, **kwargs) return _admin_required def get_or_404(cls, key): model = cls.get(key) if not model: raise NotFound() return model def get_by_key_name_or_404(cls, key_name): model = cls.get_by_key_name(key_name) if not model: raise NotFound() return model def get_by_id_or_404(cls, id): model = cls.get_by_id(id) if not model: raise NotFound() return model
login_required デコレータや、get_or_404 関数のような、Flask での AppEngine 開発がちょっとだけ便利になるユーティリティを集めています。ちりも積もればなんとやら、です。
使い方はこんな感じ。
from flask import Flask from flask_appengine import ( AppEngine, login_required, ) app = Flask(__name__) gae = AppEngine(app) # 内部で ContextProcessers がセットされる @app.route("/") @login_required def index(): return "Hello, world!"
あと、AppEngine クラスは内部で ContextProcessers を登録するので
<!DOCTYPE html> <html> <head> <title>FlaskAppEngineSample</title> </head> <body> {% if get_current_user() %} <a href="{{ create_logout_url() }}">logout</a> {% else %} <a href="{{ create_login_url() }}">login</a> {% endif %} </body> </html>
という風に、テンプレート内で Users API が使えます。これは結構便利かも。
AppEngine の全ての機能をサポートしようとしたら半端ない量になるので、バランスを考えながら機能を追加していく予定。必要最小限の機能を提供するのが Flask っぽいですから。とりあえず、Flask のセッションでデータストアを使うようにはしたいです。あと appstats のサポートも。