web-dev-qa-db-ja.com

Flask:アプリケーションとリクエスト固有の属性を設定しますか?

データベースに接続するアプリケーションを書いています。そのdb接続を一度作成してから、アプリケーションの存続期間を通してその接続を再利用したいと考えています。

ユーザーの認証も行いたい。ユーザーの認証は、リクエストの存続期間中のみ有効です。

flaskアプリの存続期間中に保存されたオブジェクトと、リクエストに固有のオブジェクトをどのように区別できますか?すべてのモジュール(およびその後のブループリント)がアクセスできるように、オブジェクトをどこに保存しますか?

これが私のサンプルアプリです:

from flask import Flask, g

app = Flask(__name__)

@app.before_first_request
def setup_database(*args, **kwargs):
    print 'before first request', g.__dict__
    g.database = 'DATABASE'
    print 'after first request', g.__dict__

@app.route('/')
def index():
    print 'request start', g.__dict__
    g.current_user = 'USER'
    print 'request end', g.__dict__

    return 'hello'

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

これを実行して(Flask 0.10.1)、http://localhost:6001/に移動すると、コンソールに次のように表示されます。

$ python app.py 
 * Running on http://127.0.0.1:6001/
 * Restarting with reloader

before first request {}
after first request {'database': 'DATABASE'}
request start {'database': 'DATABASE'}
request end {'current_user': 'USER', 'database': 'DATABASE'}
127.0.0.1 - - [30/Sep/2013 11:36:40] "GET / HTTP/1.1" 200 -

request start {}
request end {'current_user': 'USER'}
127.0.0.1 - - [30/Sep/2013 11:36:41] "GET / HTTP/1.1" 200 -

つまり、最初のリクエストは期待どおりに機能しています。flask.gはデータベースを保持しており、リクエストが開始すると、ユーザーの情報も含まれます。

しかし、2回目のリクエストで、flask.gはきれいにワイプされます!私のデータベースはどこにも見つかりません。

今、私はフラスコ.gusedがリクエストにのみ適用されることを知っています。しかし、アプリケーションにバインドされているので(0.10以降)、単一の要求だけでなく、アプリケーション全体に変数をバインドする方法を知りたいと思います。

何が欠けていますか?

編集:特にMongoDBに興味があります。私の場合は、複数のMongoデータベースへの接続を維持しています。 __init__.pyでこれらの接続を作成し、それらのオブジェクトを再利用することが最善の策ですか?

20
poundifdef

flask.gは、リクエストの期間中のみ格納します。ドキュメントでは、値はリクエストではなくアプリケーションコンテキストに格納されると述べていますが、これは実装上の問題です。flask.gのオブジェクトは同じスレッドでのみ使用可能であり、単一のリクエスト。

たとえば、データベース接続の official tutorialセクション では、接続はリクエストの最初に一度行われ、リクエストの最後で終了します。

もちろん、本当に必要な場合は、データベース接続を一度作成し、それを__init__.pyに格納して、必要に応じて(グローバル変数として)参照できます。ただし、これを行うべきではありません。接続が閉じたりタイムアウトしたりする可能性があり、複数のスレッドで接続を使用できません。

PythonでMongoを使用する方法を指定しなかったため、 PyMongo を使用すると想定します。これは、 connection pooling のすべてを処理するためです。君は。

この場合、あなたはこのようなことをするでしょう...

from flask import Flask
from pymongo import MongoClient
# This line of code does NOT create a connection
client = MongoClient()

app = Flask()

# This can be in __init__.py, or some other file that has imported the "client" attribute
@app.route('/'):
def index():
    posts = client.database.posts.find()

あなたが望むなら、このようなことをすることができます...

from flask import Flask, g
from pymongo import MongoClient
# This line of code does NOT create a connection
client = MongoClient()

app = Flask()

@app.before_request
def before_request():
    g.db = client.database

@app.route('/'):
def index():
    posts = g.db.posts.find()

これは実際にはそれほど違いはありませんが、すべてのリクエストで実行するロジックに役立ちます(ログインしているユーザーに応じてg.dbを特定のデータベースに設定するなど)。

最後に、Flaskを使用してPyMongoを設定する作業の大部分は、おそらく Flask-PyMongo で行われることを理解できます。

もう1つの質問は、ログインしているユーザーに固有のものを追跡する方法に関するものです。この場合、接続に関係するデータをいくつか保存する必要があります。 flask.gは要求の最後にクリアされるので、それは良くありません。

使用したいのは sessions です。これは、ユーザーのブラウザのCookieに(デフォルトの実装で)格納されている値を格納できる場所です。 Cookieは、ユーザーのブラウザがWebサイトに対して行うすべてのリクエストとともに渡されるため、セッションに入力したデータを利用できます。

ただし、セッションはサーバーに保存されないことに注意してください。これは、ユーザーに渡される文字列に変換されます。したがって、DB接続などを格納することはできません。代わりに識別子(ユーザーIDなど)を格納します。

ユーザー認証が機能することを確認することは、正しく行うのが非常に困難です。確認する必要があるセキュリティ上の懸念は驚くほど複雑です。 Flask-Login のようなものを使用してこれを処理することを強くお勧めします。 sessionを使用して、必要に応じて他のアイテムを保存することも、Flask-LoginにユーザーIDの決定とデータベースへの必要な値の保存を処理させ、すべてのリクエストでデータベースから取得することもできます。

つまり、要約すると、やりたいことを実行する方法はいくつかあります。それぞれに用途があります。

  • グローバルは、スレッドセーフなアイテム(PyMongoのMongoClientなど)に適しています。
  • flask.gは、リクエストの存続期間中にデータを格納するために使用できます。 SQLAlchemyベースのflaskアプリの場合、一般的なことは、after_requestメソッドを使用してリクエストの最後にすべての変更が一度に行われるようにすることです。このようなものにflask.gを使用することは非常に役立つ。
  • Flask sessionを使用して、同じユーザーからの後続のリクエストで使用できる単純なデータ(文字列と数値、接続オブジェクトではない)を保存できます。これは完全にCookieの使用に依存するため、ユーザーはいつでもCookieを削除でき、「セッション」内のすべてが失われます。したがって、関連するデータを識別するためにセッションを使用して、データベースに多くのデータを保存する必要があります。セッション中のユーザーに。
32
Mark Hildreth

「アプリケーションにバインドされている」とは、それが何を意味するかを意味するものではありません。これは、gが現在実行中のリクエストにバインドされていることを意味します。 ドキュメントを止める:

Flaskは、アクティブなリクエストに対してのみ有効であり、リクエストごとに異なる値を返す特別なオブジェクトを提供します。

Flaskのチュートリアルでは特にデータベースオブジェクトを永続化しない ですが、これは かなりのサイズのアプリケーションの標準 ではないことに注意してください。ウサギの穴を掘り下げることに本当に興味がある場合は、データベース接続プールツールをお勧めします。 ( this one など、SO上記の回答で言及)

4
cwallenpoole

セッションを使用してユーザー情報を管理することをお勧めします。セッションは、情報を複数の要求で保持するのに役立ち、flaskは、セッションフレームワークをすでに提供しています。

from flask import session
session['usename'] = 'xyz'

拡張機能 Flask-Login を見てください。ユーザー認証を処理するように設計されています。

データベースについては、 Flask-SQLAlchemy 拡張子を確認することをお勧めします。これは、初期化、プーリング、ティアダウンなどを箱から出します。必要なのは、データベースURIを構成で定義し、それをアプリケーションにバインドすることだけです。

from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
1
codegeek