Flask-Injector で試したあれこれ
Flask-Injector を試してる時に、これはどうしたら良いんだろうというのがあったので、色々試してみた。
コンストラクタに引数を渡したい
色々試していて以下のようにコンストラクタに引数を受け取りたい場合はどうしたら良いのか試行錯誤した所、binds でインスタンスを生成する際に渡してやればよさそう。
class RiakCSStorage(object): def __init__(self, config): # config に Flask の config を受け取りたい self.config = config
from injector import inject, singleton from flask import Flask from flask.ext.injector import request @inject(app=Flask) def configure(binder, app): riakcs = RiakCSStorage(app.config) binder.bind(Base, to=riakcs , scope=singleton)
インスタンスを生成して渡す。
request scope
Flask-Injector には request scope があり、これはリクエストごとにインスタンスが生成される。
が、上記のアプリケーションコンテキストである app.config を使ってインスタンスを生成した場合は singleton になるよう
from injector import inject from flask import Flask from flask.ext.injector import request @inject(app=Flask) def configure(binder, app): riakcs = RiakCSStorage(app.config) binder.bind(Base, to=riakcs , scope=request)
from injector import inject from flask import Blueprint from models.base import Base app = Blueprint('index', __name__) @app.route('/', strict_slashes=False, methods=['GET']) @inject(service=Base) def index(service): print service return service.save()
$ python app.py * Running on http://127.0.0.1:5000/ * Restarting with reloader <models.riakcs.RiakCSStorage object at 0x1071fdcd0> 127.0.0.1 - - [07/Jan/2014 23:35:57] "GET / HTTP/1.1" 200 - <models.riakcs.RiakCSStorage object at 0x1071fdcd0> 127.0.0.1 - - [07/Jan/2014 23:35:58] "GET / HTTP/1.1" 200 -
オブジェクト ID が同じ事から singleton になってると思われる。
bind する際の tips
以下のような /local にアクセスがあったら FileStorage を /riakcs にアクセスがあったら RiakCSStorage のインスタンスを使いたいみたいな場合どうすれば良いのかなと悩んだ。
from injector import inject from flask import Blueprint from models.base import Base app = Blueprint('index', __name__) @app.route('/local', strict_slashes=False, methods=['GET']) @inject(service=Base) def index(service): print service return service.save() @app.route('/riak', strict_slashes=False, methods=['GET']) @inject(service=Base) def riak(key, service): print service return service.save()
from flask import Flask from injector import inject, singleton from models.filestorage import FileStorage from models.riakcs import RiakCSStorage @inject(app=Flask) def configure(binder, app): binder.bind(Base, to=FileStorage, scope=singleton) binder.bind(Base, to=RiakCSStorage, scope=singleton)
こうやって書いてみたが、/local にアクセスしても最後に bind した RiakCSStorage のインスタンスが生成された。
よくよく考えてみたら当然か。
Base という inject 先にどっちのインスタンスを渡せば良いかなんて分からない。
以下のように Key() を使えば実現できる。
binds.py
from flask import Flask from injector import inject, singleton, Key from models.filestorage import FileStorage as FileStorageProvider from models.riakcs import RiakCSStorage as RiakCSStorageProvider FileStorage = Key('filestorage') RiakCSStorate = Key('riakcs') @inject(app=Flask) def configure(binder, app): binder.bind(FileStorage, to=FileStorageProvider, scope=singleton) binder.bind(RiakCSStorate, to=RiakCSStorageProvider, scope=singleton)
views/index.py
from injector import inject from flask import Blueprint from binds import FileStorage, RiakCSStorage app = Blueprint('index', __name__) @app.route('/local', strict_slashes=False, methods=['GET']) @inject(service=FileStorage) def index(service): print service return service.save() @app.route('/riak', strict_slashes=False, methods=['GET']) @inject(service=RiakCSStorage) def riak(key, service): print service return service.save()
これで /local にアクセスすると FileStorage のインスタンスが、/riak にアクセスすると RiakCSStorage のインスタンスが service に inject される。
一つの変数に複数のインスタンスをインジェクションする(リスト)
from flask import Flask from injector import inject, singleton, Key from models.filestorage import FileStorage from models.riakcs import RiakCSStorage Names = Key('names') @inject(app=Flask) def configure(binder, app): binder.multibind(Names, to=[FileStorage(app.config)], scope=singleton) binder.multibind(Names, to=[RiakCSStorage], scope=singleton)
from injector import inject from flask import Blueprint from binds import Names app = Blueprint('index', __name__) @app.route('/local', strict_slashes=False, methods=['GET']) @inject(service=Names) def index(service): print service return ''
$ python app.py * Running on http://127.0.0.1:5000/ * Restarting with reloader [<models.filestorage.FileStorage object at 0x10b37ec90>, <class 'models.riakcs.RiakCSStorage'>]
service にはリストが格納される。
何の役に立つのだろうかと思ったけど、シーケンシャルにロジックを実行して行くというようなのに使えそう。
一つの変数に複数のインスタンスをインジェクトする(辞書)
from flask import Flask from injector import inject, singleton, MappingKey from models.localfs import LocalFS as LocalFSProvider from models.riakcs import RiakCS as RiakCSProvider Names = MappingKey('names') @inject(app=Flask) def configure(binder, app): to = { 'local': LocalFSProvider(app.config), 'riakcs': RiakCSProvider } binder.multibind(Names, to=to, scope=singleton)
$ python app.py * Running on http://127.0.0.1:5000/ * Restarting with reloader {'local': <models.filestorage.FileStorage object at 0x1066eec90>, 'riakcs': <class 'models.riakcs.RiakCSStorage'>}
こっちは service に辞書が入る。
ユースケースが思いつかない…。