首页 > 解决方案 > teardown_appcontext 是否忽略 HTTPExceptions?

问题描述

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

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

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

如果我引发异常,则会发生回滚,但如果我使用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 函数,就会完成回滚。

标签: pythonflaskwsgiserverless

解决方案


我认为这是因为teardown_appcontext 在弹出请求上下文时调用。一个exception是 init 在request. 您可以使用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你会看到“坏节点”没有保存(回滚)。

希望这可以帮助。


推荐阅读