Blueprints & Modularity
Blueprints allow you to organize your Flask application into distinct, reusable components. Instead of defining all routes and handlers on a single Flask application object, you define them on a Blueprint and then register that blueprint with the application.
Creating and Registering Blueprints
A Blueprint is a way to group related views and other code. It provides the same decorators as the Flask object, such as @route and @before_request, because both classes inherit from flask.sansio.scaffold.Scaffold.
In a typical application factory, you create blueprints in separate modules and register them in the create_app function:
# flaskr/auth.py
from flask import Blueprint
bp = Blueprint("auth", __name__, url_prefix="/auth")
@bp.route("/register", methods=("GET", "POST"))
def register():
# ... implementation ...
return "Register"
# flaskr/__init__.py
from flask import Flask
from . import auth
def create_app():
app = Flask(__name__)
app.register_blueprint(auth.bp)
return app
When you register a blueprint, Flask records the operations you performed on it (like adding routes) and replays them on the application object.
Configuration and Precedence
Blueprints support several configuration options that affect how their routes and resources are handled:
url_prefix: Prepended to all routes in the blueprint. If the blueprint has@bp.route("/login")andurl_prefix="/auth", the final URL is/auth/login.subdomain: Routes in the blueprint will match this subdomain by default.template_folder: A folder containing templates relative to the blueprint's root path.static_folder: A folder containing static files relative to the blueprint's root path.
Template Precedence
Blueprint templates are added to the application's template search path. However, the application's own templates folder always takes precedence. If a template exists in both the app's folder and the blueprint's folder, Flask will use the one from the app.
Nested Blueprints
Flask supports nesting blueprints to create hierarchical structures. You can register a blueprint on another blueprint using Blueprint.register_blueprint.
When blueprints are nested, their url_prefix and subdomain are concatenated.
parent = Blueprint("parent", __name__, url_prefix="/parent")
child = Blueprint("child", __name__, url_prefix="/child")
@child.route("/leaf")
def leaf():
return "I am at /parent/child/leaf"
parent.register_blueprint(child)
app.register_blueprint(parent)
Internally, Blueprint.register (in src/flask/sansio/blueprints.py) handles this by updating the url_prefix and name_prefix before recursively calling register on the child blueprints:
# From src/flask/sansio/blueprints.py
if state.url_prefix is not None and bp_url_prefix is not None:
bp_options["url_prefix"] = (
state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/")
)
Application-Wide vs. Blueprint-Specific Hooks
Blueprints provide two sets of decorators for request lifecycle hooks and error handling:
- Blueprint-specific: Decorators like
@bp.before_requestor@bp.errorhandleronly trigger for requests handled by that specific blueprint. - Application-wide: Decorators like
@bp.before_app_request,@bp.after_app_request, and@bp.app_errorhandlertrigger for every request to the application, regardless of which blueprint (or the app itself) handles it.
These "app" variants are implemented using record_once to ensure they are only registered on the Flask object once, even if the blueprint is registered multiple times.
Internal Mechanism: Deferred Setup
Because a Blueprint doesn't have an application object when you define routes, it uses a "deferred" setup mechanism. When you call a method like @bp.route, it calls self.record():
# From src/flask/sansio/blueprints.py
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
The record method appends the function to self.deferred_functions. When app.register_blueprint(bp) is called, Flask creates a BlueprintSetupState object and passes it to every function in deferred_functions.
The BlueprintSetupState (found in src/flask/sansio/blueprints.py) is a temporary object that holds the registration options (like url_prefix) and provides a helper add_url_rule that automatically prefixes the endpoint with the blueprint's name.
Pitfalls and Restrictions
Unique Names
Every blueprint registered on an application must have a unique name. If you try to register two blueprints with the same name, or the same blueprint twice without providing a different name, Flask raises a ValueError.
bp = Blueprint("bp", __name__)
app.register_blueprint(bp)
app.register_blueprint(bp, name="second_copy") # OK: unique name provided
Setup Finished Restriction
Once a blueprint has been registered on an application, you can no longer call setup methods (like @route or @before_request) on it. Doing so will raise an AssertionError. This is enforced by the @setupmethod decorator which checks the _got_registered_once flag.
No Dots in Names
Blueprint names and endpoint names cannot contain dots (.). Flask uses the dot as a separator between the blueprint name and the endpoint name (e.g., auth.login) for URL generation with url_for. Passing a name with a dot to the Blueprint constructor will raise a ValueError.