Extending the Application via Blueprints
When you want to organize your application into modules but still need to handle errors or process requests globally, Flask Blueprints provide "app_" prefixed decorators to register these features across the entire application.
Registering Global Features via Blueprints
To register a feature that affects the entire application from within a module, use a Blueprint and its specific application-wide decorators. These methods record the registration and apply it to the Flask application object when register_blueprint is called.
from flask import Blueprint, render_template
# Define the blueprint
util_bp = Blueprint("util", __name__)
# This filter will be available in ALL templates, not just this blueprint's
@util_bp.app_template_filter("reverse")
def reverse_filter(s):
return s[::-1]
# This error handler will catch 404s for the entire application
@util_bp.app_errorhandler(404)
def handle_404(e):
return render_template("errors/404.html"), 404
# Register the blueprint on the app
# app.register_blueprint(util_bp)
Global Request Hooks
You can use before_app_request, after_app_request, and teardown_app_request to execute logic for every request handled by the Flask application, regardless of which blueprint or view matches the URL.
A common pattern found in the flaskr tutorial is using before_app_request to load user data into the g object:
from flask import Blueprint, g, session
# Assuming get_db is a helper to get a database connection
# from myapp.db import get_db
auth_bp = Blueprint("auth", __name__)
@auth_bp.before_app_request
def load_logged_in_user():
user_id = session.get("user_id")
if user_id is None:
g.user = None
else:
# This runs before every request in the entire app
g.user = get_db().execute(
"SELECT * FROM user WHERE id = ?", (user_id,)
).fetchone()
Global Template Utilities
Flask allows blueprints to inject variables and functions into the template context globally using app_context_processor, or register custom Jinja tests and globals.
@util_bp.app_context_processor
def inject_site_settings():
return {"site_name": "My Flask App"}
@util_bp.app_template_test("is_prime")
def is_prime(n):
if n < 2: return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0: return False
return True
@util_bp.app_template_global("get_version")
def get_version():
return "1.0.0"
These methods use record_once internally in flask.sansio.blueprints.Blueprint. This ensures that if the same blueprint is registered multiple times (for example, to mount it at different URL prefixes), the global template filters and globals are only added to the application once.
Global Error Handlers
While the standard errorhandler decorator only catches exceptions raised within the blueprint's own routes, app_errorhandler registers a handler for the entire Flask application.
errors_bp = Blueprint("errors", __name__)
@errors_bp.app_errorhandler(403)
def forbidden_handler(e):
return "You do not have permission to access this resource.", 403
@errors_bp.app_errorhandler(Exception)
def handle_unexpected_error(e):
# Log the error and return a generic message
return "An internal error occurred.", 500
Troubleshooting Blueprint Extensions
Modification After Registration
Flask prevents you from adding new routes, error handlers, or setup functions to a blueprint once it has been registered to an application. If you attempt to call a setup method like app_errorhandler after app.register_blueprint(bp), Flask will raise an AssertionError.
# This will raise an AssertionError if called after registration
bp._check_setup_finished("app_errorhandler")
Ensure all decorators and add_url_rule calls are completed before the blueprint is attached to the main application object.
Naming Restrictions
When creating a Blueprint, the name parameter cannot contain a dot (.). This is because Flask uses dots to separate blueprint names from endpoint names (e.g., blueprint_name.view_name) for URL generation.
# This will raise a ValueError
bad_bp = Blueprint("my.blueprint", __name__)
Static File Precedence
If a blueprint defines a static_folder but does not have a url_prefix, the application's main static route will take precedence. To serve blueprint-specific static files, ensure the blueprint is registered with a url_prefix or that the static_url_path is unique.
# Static files will be available at /assets/static/filename
assets_bp = Blueprint("assets", __name__, static_folder="static", url_prefix="/assets")