2013-09-30 36 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

相关问题