【问题标题】:Does teardown_appcontext ignore HTTPExceptions?teardown_appcontext 是否忽略 HTTPExceptions?
【发布时间】:2019-05-21 14:13:09
【问题描述】:

所以,我正在尝试回滚数据库会话,以防发生诸如 bad_request、未授权、禁止或 not_found 之类的 HTTP 错误。

它是一个带有 wsgi 和 flask 的无服务器应用程序。

场景是:我创建了一个要保存在数据库中的条目,但是如果发生错误,我希望它回滚会话。

如果我引发异常,则会发生回滚,但如果我使用 abort(make_response(jsonify(message=message, **kwargs), 400)),则会引发 HTTPException,但 teardown_appcontext 会忽略它。

我也试过application.config['PRESERVE_CONTEXT_ON_EXCEPTION'] = True #and false too,但没有解决我的问题。

在我的应用中:

def database(application, engine=None):
    sqlalchemy_url = os.environ.get('SQLALCHEMY_URL')
    set_session(sqlalchemy_url, engine=engine)

    @application.teardown_appcontext
    def finish_session(exception=None):
        commit_session(exception)
def commit_session(exception=None):
    if exception:
        _dbsession.rollback()
    else:
        _dbsession.commit()
    _dbsession.remove()
    if hasattr(_engine, 'dispose'):
        _engine.dispose()

在这里,如果我想返回一个 bad_request 响应,则调用该函数。 abort 函数引发了一个 HTTPException,该异常被 teardown 函数忽略了

def badrequest(message='bad request.', **kwargs):
    abort(make_response(jsonify(message=message, **kwargs), 400))

我希望 teardown_appcontext 也能识别 HTTPException,而不仅仅是一个异常。这样,如果调用了 abort 函数,就完成了回滚。

【问题讨论】:

    标签: python flask wsgi serverless


    【解决方案1】:

    我认为这是因为teardown_appcontext called when the request context is poppedexceptionrequest 的上下文中被初始化。您可以使用errorhandler()register_error_handler() 回滚会话。这是一个例子:

    from flask import Flask, abort, jsonify
    from flask_sqlalchemy import SQLAlchemy
    from werkzeug.exceptions import BadRequest
    
    app = Flask(__name__)
    app.config.update(dict(SQLALCHEMY_DATABASE_URI='...'))
    
    db = SQLAlchemy(app)
    
    
    class Node(db.Model):
       id = db.Column(db.Integer, primary_key=True)
       name = db.Column(db.String(100), nullable=False)
    
    
    @app.errorhandler(BadRequest)
    def handle_bad_request(e):
        db.session.rollback()
        return 'session has been rolled back!', 400
    
    
    @app.teardown_appcontext
    def finish_session(exception=None):
        if not exception:
            db.session.commit()
    
    
    @app.route('/bad-node')
    def bad():
        # add into session without commit and abort(see: handle_bad_request)
        db.session.add(Node(name='bad node'))
        abort(400)
    
    
    @app.route('/good-node')
    def good():
        # without exceptions - see: finish_session
        db.session.add(Node(name='good node'))
        return '<good node> was saved'
    
    
    @app.route('/nodes')
    def all_nodes():
        # just list of items from db
        return jsonify([i.name for i in Node.query.all()])
    
    
    if __name__ == '__main__':
        db.create_all()
        db.session.commit()
        app.run(debug=True)
    

    打开/good-node/bad-node 几次。之后打开/nodes 你会看到“坏节点”没有被保存(是回滚)。

    希望这会有所帮助。

    【讨论】:

    • 顺便说一下,您可以尝试在def bad()def finish_session() 中插入from flask import request。您将看到请求属性在 finish_session(teardown_appcontext) 中不可用。
    猜你喜欢
    • 2017-05-08
    • 2013-01-30
    • 2023-04-07
    • 1970-01-01
    • 2015-04-19
    • 2020-11-16
    • 1970-01-01
    • 2023-01-30
    • 2019-02-16
    相关资源
    最近更新 更多