我遇到了与Passing application context to custom converter using the Application Factory pattern类似的问题,我使用自定义URL转换器将Neo4j图形数据库ID转换为节点对象,即,
import atexit
from flask import Flask
from neo4j.v1 import GraphDatabase
from werkzeug.routing import BaseConverter
class NodeConverter(BaseConverter):
def to_python(self, value):
with driver.session() as session:
cursor = session.run('MATCH (n {id: $id}) RETURN n', id=value)
return cursor.single().values()[0]
app = Flask(__name__)
app.url_map.converters['node'] = NodeConverter
driver = GraphDatabase.driver('bolt://localhost')
atexit.register(lambda driver=driver: driver.close())
@app.route('/<node:node>')
def test(node):
print node
if __name__ == '__main__':
app.run()
字符串
虽然这种方法利用了单个数据库连接,但有几个主要缺点:i)无法通过Flask配置配置数据库连接,ii)如果数据库失败,Flask应用程序也会失败。
为了解决这个问题,我为每个http://flask.pocoo.org/docs/0.12/extensiondev/创建了一个本地扩展,即,
from flask import _app_ctx_stack, Flask
from neo4j.v1 import GraphDatabase
from werkzeug.routing import BaseConverter
class MyGraphDatabase(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
@app.teardown_appcontext
def teardown(exception):
ctx = _app_ctx_stack.top
if hasattr(ctx, 'driver'):
ctx.driver.close()
@property
def driver(self):
ctx = _app_ctx_stack.top
if ctx is not None and not hasattr(ctx, 'driver'):
ctx.driver = GraphDatabase.driver(app.config['NEO4J_URI'])
return ctx.driver
class NodeConverter(BaseConverter):
def to_python(self, value):
with app.app_context():
with db.driver.session() as session:
cursor = session.run('MATCH (n {id: $id}) RETURN n', id=value)
return cursor.single().values()[0]
db = MyGraphDatabase()
app = Flask(__name__)
app.config.from_pyfile('app.cfg')
app.url_map.converters['node'] = NodeConverter
db.init_app(app)
@app.route('/<node:node>')
def test(node):
print node
if __name__ == '__main__':
app.run()
型
这个问题是因为URL转换器在我需要包含以下块的应用程序上下文之外,
with app.app_context():
...
型
在URL解析过程中创建临时应用上下文,然后立即丢弃,从性能Angular 来看,这似乎不是最佳的。这是正确的方法吗?
这种配置的另一个问题是,当转换器和应用程序驻留在不同的文件中时,需要认识到潜在的循环引用,因为NodeConverter
需要app
,而app
注册NodeConverter
。
1条答案
按热度按时间blpfk2vs1#
在自定义URL转换器中利用Flask应用程序上下文的一种可能方法是使用
current_app
代理对象而不是app
示例。这样,您可以访问当前应用程序上下文,而无需创建临时上下文。例如,您可以修改NodeConverter
类,如下所示:字符串
请注意,我还将
db
属性更改为当前应用程序的属性,而不是全局变量。这是避免循环导入和命名空间冲突的一个很好的做法。您可以通过在MyGraphDatabase
类的init_app
方法中设置db
属性来做到这一点:型
这样,您就可以从当前应用程序上下文访问数据库驱动程序,而无需从另一个模块导入
db
对象。在自定义URL转换器中利用Flask应用程序上下文的另一种可能方法是使用
g
对象,这是一个全局命名空间,用于在单个请求上下文中存储数据。您可以将数据库驱动程序存储在before_request
处理程序中的g
对象中,然后从NodeConverter
类访问它。例如,您可以执行以下操作:型
通过这种方式,您可以避免为每个URL转换创建临时应用程序上下文,并为整个请求重用相同的数据库驱动程序。但是,您应该注意不要在其他地方修改
g
对象,因为它可能会影响转换器的行为。