Application Context & Globals
Every view function you've written so far has quietly relied on a handful of names —
request, session, current_app — that were never
imported from a concrete object and never passed in as arguments. That's not an accident;
it's Flask's context system, and understanding it clears up a whole class
of "why does this work here but not there?" confusion. This lesson explains what a context
actually is, why Flask needs one instead of a single global app variable, and
introduces current_app and g — two of the most useful names the
context system makes available.
Why Not Just Use a Global app Variable?
The simplest possible Flask setup defines app = Flask(__name__) once, at the
top of a module, and every other file that needs it writes from app import app.
For a small script this works fine. But Bookshelf doesn't stay that small, and back in
Project Structure & App Factory we
deliberately moved away from a single module-level app object, in favor of a
create_app() factory function that can build a new app instance on
demand — one configured for production, another configured for a test suite with an
in-memory database, another for a throwaway local experiment.
That flexibility creates a problem: if a utility function hardcodes
from app import app, it is permanently wired to whichever single instance
that import resolves to. It can't cooperate with a factory that produces several
independent instances over a program's lifetime. Flask's answer is to stop referring to
"the app" as a concrete object at all, and instead refer to
whichever app is currently active — resolved fresh, every time, based on
what's actually running. That's exactly what current_app does.
current_app: A Proxy to Whichever App Is Running
current_app is what Flask calls a context-local proxy — from
your code, it looks and behaves like an ordinary object, but every attribute access it
receives is silently forwarded to the real, concrete Flask instance that is
currently handling things. You import it once, from the flask package itself,
and it works correctly no matter which app instance a factory happens to have built:
# utils.py
from flask import current_app
def upload_path(filename):
upload_folder = current_app.config['UPLOAD_FOLDER']
return f"{upload_folder}/{filename}"
Notice what's not here: upload_path() never received the actual
app object as an argument, and it never imported one directly. Whatever app
is currently active — the one built by create_app() for this run, whether
that's the production instance, a test instance, or a local dev instance — is the one
whose config['UPLOAD_FOLDER'] this function reads. Drop this same function
into any Bookshelf module, call it from any view, and it always resolves to the right app.
current_app whenever a helper function, model method, or CLI command needs the app's configuration but shouldn't have to accept the app object as a parameter just to get it.Two Kinds of Context: Request vs. Application
Flask actually manages two related, but distinct, kinds of context, and it's worth keeping them straight:
| Context | What it makes available |
|---|---|
| Request context | request (the incoming HTTP request) and session (the visitor's session data — see Sessions & Cookies) |
| Application context | current_app (the active Flask instance) and g (a scratch space for the duration of one request, covered below) |
The two are related in a specific way: a request context cannot exist without an application context, because Flask needs to know which app's configuration and URL map to use in order to make sense of a request in the first place. So whenever Flask pushes a request context, it automatically pushes a matching application context right along with it. The reverse isn't true — you can have an active application context with no request in sight at all, which is precisely the scenario the last section of this lesson covers.
What Gets Pushed, and When
During ordinary operation — a visitor's browser hitting one of Bookshelf's routes — you never see this happen, because Flask manages it for you automatically:
- A request arrives at the WSGI server.
- Flask pushes a request context for it, which in turn pushes a matching application context.
- Your view function runs. Inside it (and inside any template it renders),
request,session,current_app, andgare all available. - Flask builds the response, then pops both contexts back off — cleaning the slate for the next request.
This is also why none of these names work in plain module-level code, outside of a view
function or a request-triggered callback — there's no active context yet for them to read
from. Trying to access current_app.config at import time, before any request
has ever come in and outside of the manual technique shown further down, raises a
RuntimeError telling you exactly that: there's no application context to work
with.
g: Per-Request Scratch Space
g (short for "global", though it's anything but — more on that in a moment)
is a plain object Flask hands you for stashing arbitrary data during a single
request, so that data is cheaply available to other functions later in that same
request without threading it through as a parameter everywhere. A typical use in Bookshelf:
look up the logged-in visitor's book count once, early in the request, and let every later
piece of code — a template, a context processor, another view helper — read it straight
off g instead of re-querying the database:
# app.py
from flask import g, session
@app.before_request
def load_book_count():
if 'user_id' in session:
g.book_count = Book.query.filter_by(
user_id=session['user_id']
).count()
else:
g.book_count = None
@app.before_request registers a function that Flask runs before every single
view, so g.book_count gets set once per request, right at the start. Any
template rendered later in that same request can now read it directly:
{% if g.book_count is not none %}
<p>You have {{ g.book_count }} books across your shelves.</p>
{% endif %}
And any other view-side code — a second helper function called later in the same request,
for instance — can read g.book_count too, with no need to pass it around as a
function argument. That's the whole value of g: a shared scratch space for the
lifetime of one request, reachable from anywhere.
g Is Reset on Every Request — It Is Not a Cache
g sticks around for the next visitor, or the next request from the same visitor. It does not. Flask creates a brand-new g for every single request and discards the old one when the request ends — anything you put on it is gone the moment the response is sent. If you need data to persist across multiple requests — a computed value that's expensive to recompute and safe to reuse for a while — that's a job for an actual caching layer, covered in Caching, not for g.Manually Pushing an Application Context Outside a Request
Sometimes you need current_app or database access with no HTTP request
involved at all — a one-off maintenance script, a data-import job, or a quick check run
from a Python shell. In these situations there's no request coming in to trigger Flask's
automatic context-pushing, so you push an application context yourself, using
app.app_context() as a context manager:
# count_books.py — a one-off script, not run as part of any web request
from app import create_app
from models import Book
app = create_app()
with app.app_context():
total = Book.query.count()
print(f"Bookshelf has {total} books across all users.")
create_app() is the same app-factory function introduced in
Project Structure & App Factory —
building the app is still required, since an application context always belongs to a
specific app instance. Once inside the with block, current_app
resolves correctly and any code that touches the database (which relies on the app's
configuration) works exactly as it would inside a real request. Outside the block, the
context is automatically popped, and using current_app again would raise the
same "outside of application context" error mentioned earlier.
flask shell, custom flask subcommands) push an application context for you automatically — you mostly need app.app_context() by hand only in standalone scripts like the one above.Quick Reference: What's Available, and From Which Context
| Name | Provided by | Typical use |
|---|---|---|
current_app | Application context | Reading config, e.g. current_app.config['UPLOAD_FOLDER'] |
g | Application context | Sharing data across functions within one request |
request | Request context | Reading form data, query strings, headers |
session | Request context | Reading/writing the visitor's session data |
current_user turns out to be another context-local proxy, working exactly like current_app.