Skip to main content

Tutorial: Managing Resources with g

In this tutorial, you will build a resource management system for a Flask application. You will learn how to use the g object to store and reuse resources like database connections during a single request, and how to ensure those resources are properly cleaned up using teardown functions.

Prerequisites

To follow this tutorial, you should have a basic Flask application structure. This example uses sqlite3 for the database.

Step 1: Lazy-Loading a Database Connection

The g object is an instance of _AppCtxGlobals. It serves as a namespace for storing data during an application context. A common pattern is to store a database connection in g so that multiple functions can access the same connection without creating new ones.

Create a file named db.py and implement a get_db function:

import sqlite3
from flask import current_app, g

def get_db():
"""Connect to the application's configured database. The connection
is unique for each request and will be reused if this is called
again.
"""
if "db" not in g:
g.db = sqlite3.connect(
current_app.config["DATABASE"],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row

return g.db

In this step, you check if "db" is already present in g using the in operator (implemented by _AppCtxGlobals.__contains__). If it isn't, you create the connection and assign it to g.db. Subsequent calls to get_db within the same request will return the existing connection.

Step 2: Implementing Automatic Teardown

Resources stored in g must be closed when the request ends. The AppContext class manages this lifecycle. When a context is popped, it triggers functions registered with teardown_appcontext.

Add a close_db function to db.py and a registration function:

def close_db(e=None):
"""If this request connected to the database, close the
connection.
"""
db = g.pop("db", None)

if db is not None:
db.close()

def init_app(app):
"""Register database functions with the Flask app."""
app.teardown_appcontext(close_db)

The g.pop() method (from _AppCtxGlobals.pop) safely removes the attribute if it exists. By calling app.teardown_appcontext(close_db), you tell Flask to call close_db every time the application context is destroyed, even if an unhandled exception occurred.

Step 3: Storing Request-Specific State

You can also use g to store information about the current user or other request-specific state. This is typically done in a before_request hook.

In your authentication module (e.g., auth.py), you can load the user:

from flask import Blueprint, g, session
from .db import get_db

bp = Blueprint("auth", __name__)

@bp.before_app_request
def load_logged_in_user():
"""If a user id is stored in the session, load the user object
from the database into g.user.
"""
user_id = session.get("user_id")

if user_id is None:
g.user = None
else:
g.user = (
get_db().execute("SELECT * FROM user WHERE id = ?", (user_id,)).fetchone()
)

By assigning the user to g.user, it becomes available to all other view functions and templates during the rest of the request.

Step 4: Accessing g in Tests and CLI

The g object is only accessible when an AppContext is active. If you try to access it outside of a request or a CLI command, Flask will raise a RuntimeError. In tests or scripts, you must manually push a context.

def test_db_connection(app):
with app.app_context():
# Now g is available
db = get_db()
assert "db" in g

# After the 'with' block, the context is popped and close_db is called

When the with app.app_context(): block exits, the AppContext.pop() method is called, which executes your close_db function and clears the g namespace.

Summary

By the end of this tutorial, you have implemented:

  1. Lazy-loading: Using g to create a database connection only when needed.
  2. Resource Reuse: Accessing the same g.db instance throughout a request.
  3. Safe Cleanup: Using teardown_appcontext to ensure connections are closed.
  4. Global State: Using g.user to share the current user across your application.

For next steps, consider exploring how AppContext handles nested pushes or how to use g.setdefault() to initialize complex objects safely.