20

我正在编写一个连接到数据库的应用程序。我想创建一次该数据库连接,然后在应用程序的整个生命周期中重用该连接。

我还想对用户进行身份验证。用户的身份验证将仅在请求的生命周期内有效。

如何区分为烧瓶应用程序的生命周期存储的对象与特定于请求的对象?我会将它们存储在哪里,以便所有模块(和后续蓝图)都可以访问它们?

这是我的示例应用程序:

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正在保存我的数据库,并且当请求开始时,它也有我的用户信息。

然而,根据我的第二个要求,flask.g 被擦干净了!我的数据库无处可寻。

现在,我知道 flask.g过去只适用于请求。但是现在它已绑定到应用程序(从 0.10 开始),我想知道如何将变量绑定到整个应用程序,而不仅仅是单个请求。

我错过了什么?

编辑:我对 MongoDB 特别感兴趣——就我而言,维护与多个 Mongo 数据库的连接。我最好的选择是在其中创建这些连接__init__.py并重用这些对象吗?

4

3 回答 3

32

flask.g只会在请求期间存储东西。文档提到值存储在应用程序上下文而不是请求中,但这更多是一个实现问题:它不会改变对象flask.g仅在同一个线程中可用的事实,并且在一个单个请求。

例如,在数据库连接的官方教程部分中,连接在请求开始时建立一次,然后在请求结束时终止。

当然,如果您真的愿意,您可以创建一次数据库连接,将其存储在 中__init__.py,并根据需要引用它(作为全局变量)。但是,您不应该这样做:连接可能会关闭或超时,并且您不能在多个线程中使用该连接。

由于您没有指定如何在 Python 中使用 Mongo,因此我假设您将使用PyMongo,因为它会为您处理所有连接池

在这种情况下,你会做这样的事情......

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在请求结束时被清除,所以这不好。

您要使用的是会话。您可以在此处存储(使用默认实现)存储在用户浏览器上的 cookie 中的值。由于 cookie 将与用户浏览器向您的网站发出的每个请求一起传递,因此您将获得您在会话中输入的数据。

但请记住,会话不会存储在服务器上。它变成一个字符串,来回传递给用户。因此,您不能将诸如数据库连接之类的内容存储在其上。您将改为存储标识符(如用户 ID)。

确保用户身份验证有效是非常困难的。您需要确保的安全问题非常复杂。我强烈建议您使用Flask-Login 之类的东西来为您处理这个问题。您仍然可以session根据需要使用 来存储其他项目,或者您可以让 Flask-Login 处理确定用户 ID 并将您需要的值存储在数据库中,并在每个请求中从数据库中检索它们。

所以,总而言之,有几种不同的方法可以做你想做的事。各有各的用途。

  • 全局变量适用于线程安全的项目(例如 PyMongo 的 MongoClient)。
  • flask.g可用于在请求的生命周期中存储数据。使用基于 SQLAlchemy 的烧瓶应用程序,通常要做的事情是确保在使用after_request方法的请求结束时立即发生所有更改。用于flask.g这样的事情非常有帮助。
  • Flasksession可用于存储可用于来自同一用户的后续请求的简单数据(字符串和数字,而不是连接对象)。这完全取决于使用 cookie,因此用户在任何时候都可以删除 cookie,“会话”中的所有内容都将丢失。因此,您可能希望将大部分数据存储在数据库中,会话用于识别与会话中的用户相关的数据。
于 2013-09-30T20:29:05.747 回答
4

“绑定到应用程序”并不意味着您认为它意味着什么。这意味着g绑定到当前正在运行的请求。引用文档:

Flask 为您提供了一个特殊的对象,确保它仅对活动请求有效,并且将为每个请求返回不同的值。

应该注意的是,Flask 的教程并没有专门持久化数据库对象,但这对于任何规模很大的应用程序都不是规范的。如果您真的对深入研究兔子洞感兴趣,我建议您使用数据库连接池工具。(例如这个,在上面提到的SO答案中提到)

于 2013-09-30T15:59:09.830 回答
1

我建议你使用 session 来管理用户信息。会话可以帮助您保存多个请求的黑白信息,并且烧瓶已经为您提供了一个会话框架。

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)
于 2013-09-30T16:16:03.287 回答