Application context, request context and g

Flask · Advanced · 12 min

"Working outside of application context" is the most common Flask error, and contexts explain it.

Flask avoids passing the app and request into every function by using contexts. During a request, Flask pushes:

  • the application context, which makes current_app and g available
  • the request context, which makes request and session available

Both are popped when the response is sent. Outside a request, for example in a script, a CLI command, a background thread or the Python shell, there is no context, and touching current_app raises RuntimeError: Working outside of application context. Push one yourself:

with app.app_context():
    init_db()

CLI commands registered with @app.cli.command() already run inside an app context. For code that needs request or url_for with a host, use app.test_request_context("/path").

`g` is a namespace object for data scoped to one app context, which in practice means one request. It is the right place for per-request resources: the current user loaded from the session, or a database connection opened lazily on first use.

def get_db():
    if "db" not in g:
        g.db = connect()
    return g.db

Clean up with @app.teardown_appcontext, which runs when the context ends, even after an exception.

Don't store anything on g expecting it to survive to the next request; it doesn't. Use the session or a database for that. And don't stash request or current_app in a global or pass them to another thread: they are proxies that only work while their context is active.

Example

import sqlite3
from flask import Flask, current_app, g

app = Flask(__name__)
app.config["DATABASE"] = ":memory:"

def get_db():
    if "db" not in g:                       # open lazily, once per context
        g.db = sqlite3.connect(current_app.config["DATABASE"])
    return g.db

@app.teardown_appcontext
def close_db(exc):
    db = g.pop("db", None)
    if db is not None:
        db.close()
@app.get("/")
def index():
    get_db(); get_db()                      # second call reuses the connection
    return {"sum": get_db().execute("select 1 + 1").fetchone()[0]}
print(app.test_client().get("/").json)   # {'sum': 2}
try:
    current_app.name
except RuntimeError as e:
    print(str(e).splitlines()[0])        # Working outside of application context.

Remember

  • During a request, current_app/g and request/session are available
  • Outside a request, push one with app.app_context() or test_request_context()
  • g holds per-request resources; clean up in teardown_appcontext

Watch out for

  • Expecting values on g to persist across requests.

Check yourself

  1. A script calls a helper that uses current_app.config and fails with "Working outside of application context". What fixes it?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: flask.palletsprojects.com/en/stable/appcontext/