dexpiper
dexpiper

Reputation: 91

Access database from Flask blueprint

I'm building a RESTful API application using Flask and SQLAlchemy and try to apply blueprint pattern to register my routes. Blueprint @route functions contain references to database session object. I wish to get access to it "proxy style" - in a way you can access Flask.app with app.current_app, but my implementation doesn't work.

wsgi.py: (run from here)

# imports
from wallet.walletapp import main

app = create_app(url='sqlite:///./test.db')
dbase = Database(url='sqlite:///./test.db')

db = dbase.create_session()
models.Base.metadata.create_all(bind=dbase.engine)

abort = Abort(app, db)
app.config['database'] = db
app.config['abortion'] = abort
with app.app_context:
    app.register_blueprint(main)
app.run(debug=True)

walletapp.py

main = Blueprint('main', __name__)
app = current_app
db = current_app.config['database']
abort = current_app.config['abortion']

# Get wallet list
@main.route('/v1/wallets', methods=['GET'])
def get_wallets():
    '''
    Get all the wallets and their balances
    '''
    wallets = db.query(models.Wallet).all()
    result = wallets_schema.dump(wallets)

    resp = cook_response(app, {'wallets': result})

    return resp, 200

This way I get a RuntimeError: Working outside of application context early - on importing stage in wsgi.py:

  File "/wsgi.py", line 5, in <module>
        from wallet.walletapp import main
  File "/walletapp.py", line 26, in <module>
        db = current_app.config['database']
  File "/env/lib/python3.9/site-packages/werkzeug/local.py", line 422, in __get__
        obj = instance._get_current_object()
  File "/env/lib/python3.9/site-packages/werkzeug/local.py", line 544, in _get_current_object
        return self.__local()  # type: ignore
  File "env/lib/python3.9/site-packages/flask/globals.py", line 47, in _find_app
        raise RuntimeError(_app_ctx_err_msg)

The idea behind this is to make walletapp.py more flexible as a real blueprint: for example, to easily swap working database to dummy one in unit tests.

In summary:

Defining or importing database (db) inside walletapp.py makes module unpluggable: I can work only with database imported or stated in it. How can I rewrite my code to make it working as expected?

This question has a direct connection with mine, but answers give not mush: use Flask-SQLAlchemy or "declare the database object before the context and then import it", which I tried to implement here. I do not wish to use Flask-SQLAlchemy pattern (because of some points described in this article).

I am new to Flask, so please be kind if question is obsolete, excessive or wrong otherwise) I feel there should be some sound and crisp solution, but could not touch the way to it.

Upvotes: 3

Views: 2651

Answers (1)

dexpiper
dexpiper

Reputation: 91

I found a solution. Would post it here in case somebody will stuck with the same kind of question.

Firstly, I have got an error here:

app.app_context:

I should have called the context like

app.app_context():

Thanks for answer.

Secondly, I should have imported my routes from wsgi.py inside app.app_context():

# defining app and db
abort = Abort(app, db)
app.config['database'] = db
app.config['abortion'] = abort
with app.app_context():
    from wallet import walletapp
    app.register_blueprint(walletapp.main)
app.run(debug=True)

And for last, I rewrote walletapp.py like this:

app = current_app
    with app.app_context():
        db = current_app.config['database']
        abort = current_app.config['abortion']
        main = Blueprint('main', __name__)
# then goes @main.routes

In this pattern code works well.

Upvotes: 5

Related Questions