Storing Data During a Request with g
In this tutorial, you will learn how to use the g object to store and share data across different functions during a single request. You will build a pattern for lazy-loading a database connection and sharing an authenticated user object between middleware and view functions.
The g object is a proxy to an instance of _AppCtxGlobals. It acts as a namespace for storing data during an application context.
Prerequisites
To follow this tutorial, you need the flask package installed. You should have a basic Flask application structure:
from flask import Flask, g, session, current_app
import sqlite3
app = Flask(__name__)
app.config["DATABASE"] = "example.db"
Step 1: Storing Simple Data
The simplest way to use g is by assigning attributes to it. These attributes are available for the duration of the current application context (usually the lifetime of a single request).
@app.before_request
def set_request_start():
# Store a simple value in g
g.request_time = "2023-10-27T10:00:00"
@app.route("/")
def index():
# Access the value stored in g
return f"Request started at: {g.request_time}"
When you assign g.request_time, the _AppCtxGlobals.__setattr__ method stores the value in the object's internal dictionary. This data is unique to the current request and won't leak to other concurrent requests.
Step 2: Lazy-Loading a Database Connection
A common pattern in this codebase, as seen in examples/tutorial/flaskr/db.py, is using g to manage resources like database connections. This ensures the connection is only created if needed and is reused throughout the request.
Create a get_db function that checks g before creating a new connection:
def get_db():
# Check if 'db' is already in the g namespace
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 example, get_db uses the __contains__ implementation of _AppCtxGlobals ("db" not in g) to verify if the connection exists. If not, it initializes it and stores it in g.db.
Step 3: Sharing Data Across Hooks
You can use g to pass data from a setup hook to your view functions. This is frequently used for authentication, as demonstrated in examples/tutorial/flaskr/auth.py.
@app.before_request
def load_logged_in_user():
user_id = session.get("user_id")
if user_id is None:
g.user = None
else:
# Use the get_db function from Step 2
g.user = get_db().execute(
"SELECT * FROM user WHERE id = ?", (user_id,)
).fetchone()
By setting g.user in a before_request hook, every view function and even your Jinja2 templates can access the current user via g.user without needing to query the database again.
Step 4: Safe Data Access and Cleanup
The _AppCtxGlobals class provides dict-like methods such as get(), pop(), and setdefault() for more robust data handling. You can use these to safely retrieve or remove data.
To ensure resources are cleaned up, use the teardown_appcontext decorator. This is where you should remove and close resources stored in g.
@app.teardown_appcontext
def close_db(exception=None):
# Use pop() to safely get and remove the 'db' attribute
db = g.pop("db", None)
if db is not None:
db.close()
The g.pop("db", None) call uses the _AppCtxGlobals.pop method, which removes the attribute from the internal dictionary and returns it, or returns None if it doesn't exist.
Summary
By the end of this tutorial, you have implemented a robust data-sharing pattern:
gprovides a request-local namespace.- Lazy-loading with
get_dbprevents unnecessary resource allocation. before_requesthooks populategwith common data like the current user.teardown_appcontextensures that resources stored ingare properly closed using methods likepop().
Next, you might explore how g is accessible directly within your Jinja2 templates, allowing you to use {{ g.user.username }} without passing it explicitly to render_template.