2013-09-30 40 views
14

我正在寫一個連接到數據庫的應用程序。我想創建一次數據庫連接,然後在應用程序的整個生命週期中重用該連接。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) 

當我運行這個(瓶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持有我的數據庫,並且當請求開始時,它也具有我的用戶信息。

但是,經我第二次請求,flask.g被擦乾淨了!我的數據庫無處可查。

現在,我知道flask.g 使用只適用於請求。但現在它已經綁定到應用程序(從0.10開始),我想知道如何將變量綁定到整個應用程序,而不僅僅是一個請求。

我錯過了什麼?

編輯:我特別感興趣的MongoDB - 在我的情況下,維護連接到多個Mongo數據庫。我最好只在__init__.py中創建這些連接並重用這些對象?

回答

26

flask.g只會在請求期間存儲內容。該文檔提到這些值存儲在應用程序上下文中而不是請求中,但這更多的是實現問題:它不會改變flask.g中的對象僅在同一線程中可用的事實,並且在生命週期一個請求。

例如,在official tutorial section on database connections中,連接在請求開始時進行一次,然後在請求結束時終止。

當然,如果您真的想,您可以創建一次數據庫連接,將其存儲在__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中完成。

您的其他問題涉及如何跟蹤特定於登錄用戶的內容。那麼,在這種情況下,您需要存儲一些與連接有關的數據。 flask.g在reuquest結束時被清除,所以這沒有用。

你想要使用的是sessions。這是一個地方,您可以將存儲在cookie中的值(使用默認實現)存儲在用戶的瀏覽器中。由於Cookie會隨用戶瀏覽器對您網站的每個請求一起傳遞,因此您將可以獲得您放入會話中的數據。

請記住,會話不存儲在服務器上。它變成一個傳遞給用戶的字符串。因此,您不能將數據庫連接等內容存儲到它上面。您將改爲存儲標識符(如用戶標識)。

確保用戶身份驗證的工作原理非常難以正確。您需要確保的安全性問題非常複雜。我強烈建議使用類似Flask-Login的東西來處理這個問題。您仍然可以根據需要使用session來存儲其他項目,或者您可以讓Flask-Login句柄確定用戶標識,並將所需的值存儲在數據庫中,並在每個請求中從數據庫中檢索這些值。

總之,有幾種不同的方法可以做你想做的事情。每個人都有他們的用法。

  • Globals適合線程安全的項目(例如PyMongo的MongoClient)。
  • flask.g可用於在請求的生命週期中存儲數據。使用基於SQLAlchemy的燒瓶應用程序時,常見的做法是確保在使用after_request方法的請求結束時立即發生所有更改。對於這樣的東西使用flask.g是非常有幫助的。
  • Flask session可用於存儲可用於來自同一用戶的後續請求的簡單數據(字符串和數字,而不是連接對象)。這完全依賴於使用cookie,因此用戶可以在任何時候刪除cookie,並且「會話」中的所有內容都將丟失。因此,您可能希望將大部分數據存儲在數據庫中,並使用會話來標識會話中與用戶相關的數據。
+0

關於'flask.g':「從Flask 0.10開始,它存儲在應用程序上下文中,不再存在於請求上下文中,這意味着如果只有應用程序上下文被綁定而不是請求,它就變爲可用。 - 燒瓶文件http://flask.pocoo.org/docs/0.12/api/#flask.g – Raymond

4

「綁定到應用程序」並不意味着你的想法。這意味着g被綁定到當前正在運行的請求。 Quoth the docs:

Flask爲您提供了一個特殊對象,它確保它僅對活動請求有效,並且會爲每個請求返回不同的值。

應該注意的是,Flask's tutorials specifically do not persist database objects,但這不是normative for any application of substantial size。如果你真的有興趣潛入兔子洞,我建議一個數據庫連接池工具。 (如this one,在上面的ref回答中提到)

1

我建議你使用會話來管理用戶信息。會話可以幫助您保留多個請求的信息,而燒瓶已經爲您提供了一個會話框架。

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) 
+0

僅供參考,您不能在請求之外訪問「會話」。 – Lucas

相關問題