Teardown_appcontext игнорирует исключения HTTPException? - PullRequest
0 голосов
/ 21 мая 2019

Итак, я пытаюсь откатить сеанс базы данных в случае возникновения ошибки HTTP, такой как bad_request, неавторизованный, запрещенный или not_found.

это приложение без сервера с 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.Функция прерывания вызывает HTTPException, который игнорируется функцией teardown

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

Я хочу, чтобы teardown_appcontext также распознавал HTTPException, а не только Exception.Таким образом, если вызывается функция прерывания, откат будет выполнен.

1 Ответ

2 голосов
/ 21 мая 2019

Я думаю, это потому, что teardown_appcontext вызывается, когда контекст запроса выталкивается .exception был инициирован в контексте 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, и вы увидите, что «плохие узлы» не были сохранены (был откат).

Надеюсь, это поможет.

...