在自定义URL转换器中利用Flask应用程序上下文

时间:2017-07-15 00:56:54

标签: python flask neo4j werkzeug

我遇到了与Passing application context to custom converter using the Application Factory pattern类似的问题,我使用自定义网址转换器将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解析期间创建临时应用程序上下文,然后立即丢弃,从性能角度来看似乎不是最理想的。这是正确的方法吗?

此配置的另一个问题是,当转换器和应用程序驻留在不同的文件中时,需要认识到潜在的循环引用,因为NodeConverter需要appapp注册NodeConverter

0 个答案:

没有答案