Blueprints & Modular Design
Blueprints are a modular way to organize Flask applications by grouping related views, templates, and static files. They allow developers to define application components without requiring an immediate application object, making them essential for building scalable and maintainable architectures.
In this codebase, the Blueprint class (found in src/flask/blueprints.py and src/flask/sansio/blueprints.py) inherits from Scaffold, providing it with the same routing and configuration capabilities as the main Flask application object.
Core Concepts
The Blueprint Class
A Blueprint is defined by a unique name and an import_name (usually __name__). The name is used to prefix all endpoints defined within the blueprint, which prevents naming collisions across different modules.
# From examples/tutorial/flaskr/auth.py
from flask import Blueprint
bp = Blueprint("auth", __name__, url_prefix="/auth")
When a blueprint is initialized, it records operations (like route registrations) in a list called deferred_functions. These operations are only applied to the application when the blueprint is registered.
Registration and Setup State
The registration process is handled by app.register_blueprint(). Internally, this method calls the blueprint's register method, which creates a BlueprintSetupState object. This state object carries configuration options—such as url_prefix and subdomain—and applies the deferred functions to the Flask application instance.
# From src/flask/sansio/blueprints.py
class BlueprintSetupState:
def __init__(self, blueprint, app, options, first_registration):
self.app = app
self.blueprint = blueprint
self.options = options
self.first_registration = first_registration
# ... resolves url_prefix and subdomain ...
Modular Application Structure
Application Factories
Blueprints are typically registered within an application factory function. This pattern allows for creating multiple instances of the same application with different configurations.
# From examples/tutorial/flaskr/__init__.py
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
# ... configuration ...
from . import auth, blog
app.register_blueprint(auth.bp)
app.register_blueprint(blog.bp)
return app
Middleware and Handlers
Blueprints support both local and global middleware.
@bp.before_request: Runs only before requests handled by this blueprint.@bp.before_app_request: Runs before every request in the entire application, regardless of which blueprint handles it.
Example of a global handler defined within a blueprint:
# From examples/tutorial/flaskr/auth.py
@bp.before_app_request
def load_logged_in_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()
Advanced Modularization
Nesting Blueprints
Blueprints can be registered on other blueprints. This allows for hierarchical URL structures where prefixes and subdomains are applied cumulatively.
# From tests/test_blueprints.py
parent = Blueprint("parent", __name__)
child = Blueprint("child", __name__)
grandchild = Blueprint("grandchild", __name__)
child.register_blueprint(grandchild, url_prefix="/grandchild")
parent.register_blueprint(child, url_prefix="/child")
app.register_blueprint(parent, url_prefix="/parent")
# Resulting URL for a route in grandchild: /parent/child/grandchild/route
Resource Management
Each blueprint can have its own static_folder and template_folder.
- Templates: Blueprint templates are added to the application's template search path but have lower precedence than the main application's templates.
- Static Files: If a
static_folderis provided, a route is automatically registered to serve files from that directory.
# From src/flask/sansio/blueprints.py
if self.has_static_folder:
state.add_url_rule(
f"{self.static_url_path}/<path:filename>",
view_func=self.send_static_file,
endpoint="static",
)
Constraints and Best Practices
- Naming Restrictions: Blueprint names cannot contain dots (
.). This is because dots are used as delimiters for endpoint names (e.g.,auth.login). - Registration Lock: Once a blueprint is registered with an application, its
_got_registered_onceflag is set toTrue. Attempting to call setup methods like@bp.routeafter registration will raise anAssertionError. - URL Prefixing: Using
url_prefixin theBlueprintconstructor or duringregister_blueprintis the recommended way to namespace routes and avoid collisions. - Endpoint Resolution: When using
url_forwithin a blueprint, you can use a leading dot (e.g.,url_for(".login")) to refer to an endpoint within the same blueprint. Otherwise, the full name (e.g.,url_for("auth.login")) must be used.