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.

Rule of thumb: reach for 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:

ContextWhat it makes available
Request contextrequest (the incoming HTTP request) and session (the visitor's session data — see Sessions & Cookies)
Application contextcurrent_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:

  1. A request arrives at the WSGI server.
  2. Flask pushes a request context for it, which in turn pushes a matching application context.
  3. Your view function runs. Inside it (and inside any template it renders), request, session, current_app, and g are all available.
  4. 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

Note: it's tempting to assume something stashed on 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.

Where you'll actually use this: Flask's own CLI commands (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

NameProvided byTypical use
current_appApplication contextReading config, e.g. current_app.config['UPLOAD_FOLDER']
gApplication contextSharing data across functions within one request
requestRequest contextReading form data, query strings, headers
sessionRequest contextReading/writing the visitor's session data
Coming up next: with contexts demystified, we can tackle a topic that depends on them heavily — Authentication with Flask-Login, where current_user turns out to be another context-local proxy, working exactly like current_app.