When scattering Flask Models, RuntimeError: 'application not registered on db' was raised

Question:

I am re-factoring my Flask application by scattering the models, blueprints but I am having a runtime error.

def create_app():
    app = flask.Flask("app")
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
    app.register_blueprint(api)
    db.init_app(app)
    db.create_all()
    return app

I have the following problem(the sample project are hosted here: https://github.com/chfw/sample):

Traceback (most recent call last):
  File "application.py", line 17, in <module>
    app = create_app()
  File "application.py", line 12, in create_app
    db.create_all()
  File "AppDataRoamingPythonPython27site-packagesflask_sqlalchemy__init__.py", line 856, in create_all
    self._execute_for_all_tables(app, bind, 'create_all')
  File "AppDataRoamingPythonPython27site-packagesflask_sqlalchemy__init__.py", line 836, in _execute_for_all_tables
    app = self.get_app(app)
  File "AppDataRoamingPythonPython27site-packagesflask_sqlalchemy__init__.py", line 809, in get_app
    raise RuntimeError('application not registered on db 
           'RuntimeError: application not registered on db 
            instance and no application bound to current context

I did a research on this topic. The re-factoring is suggested here:

Flask-SQLAlchemy import/context issue

The same problem was raised here:

http://flask.pocoo.org/mailinglist/archive/2010/8/30/sqlalchemy-init-app-problem/#b1c3beb68573efef4d6e571ebc68fa0b

And the above thread(2010) suggested a hack like this:

    app.register_blueprint(api)
    db.app=app #<------------<<
    db.init_app(app)

Did anyone know how to do this properly? How did you solve it?

Thanks

Asked By: chfw

||

Answers:

This has to do with Flask’s application context. When initialized with db.init_app(app), Flask-SQLAlchemy doesn’t know which app is the “current” app (remember, Flask allows for multiple apps in the same interpreter). You could have multiple apps using the same SQLAlchemy instance in the same process, and Flask-SQLAlchemy would need to know which is the “current” one (due to Flask’s context local nature of everything).

If you need to do this during runtime, you must explicitly say which app is the “current” app for all calls. You can do this by changing your code to use a with app.app_context() block:

def create_app():
    app = flask.Flask("app")
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
    app.register_blueprint(api)
    db.init_app(app)
    with app.app_context():
        # Extensions like Flask-SQLAlchemy now know what the "current" app
        # is while within this block. Therefore, you can now run........
        db.create_all()

    return app

If you are writing a standalone script that needs the app context, you can push the context at the beginning rather than putting everything in a with block.

create_app().app_context().push()

If you write a command for Flask’s cli the command will automatically have access to the context.

Answered By: Mark Hildreth

Mark’s answer was great and it helped me a lot. However, another way to approach this is to run the code that relies on the app context in a function decorated with @app.before_first_request. See http://flask.pocoo.org/docs/0.10/appcontext/ for more information. That’s in fact how I ended up doing it, largely because I wanted to be able to call the initialization code outside of flask as well, which I handle this way.

In my case I want to be able to test SQLAlchemy models as plain SQLAlchemy models without Flask-SQLAlchemy, though the db in the code below is simply a (Flask) SQLAlchemy db.

@app.before_first_request
def recreate_test_databases(engine = None, session = None):
  if engine == None:
    engine = db.engine
  if session == None:
    session = db.session

  Base.metadata.drop_all(bind=engine)
  Base.metadata.create_all(bind=engine)
  # Additional setup code
Answered By: John Lockwood
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.