Nested Blueprints
Nested blueprints allow you to organize a large application into a hierarchy of modules. By registering one blueprint on another, you can create complex URL structures, share subdomains, and manage namespaced endpoints and error handlers.
Registering Nested Blueprints
To nest a blueprint, use the register_blueprint method on an existing Blueprint instance. This is similar to how you register a blueprint on the Flask application object.
from flask import Blueprint
parent = Blueprint("parent", __name__)
child = Blueprint("child", __name__)
# Nesting the child blueprint under the parent
parent.register_blueprint(child, url_prefix="/child")
When the parent blueprint is eventually registered on the application, all its nested blueprints are registered as well.
URL Prefix Inheritance
URL prefixes are concatenated from the top down. If a parent blueprint has a prefix and the child blueprint also defines one (either at creation or during registration), the resulting URL path will be the combination of both.
In tests/test_blueprints.py, this hierarchy is demonstrated:
parent = Blueprint("parent", __name__)
child = Blueprint("child", __name__)
grandchild = Blueprint("grandchild", __name__)
@grandchild.route("/")
def grandchild_index():
return "Grandchild"
# Resulting path: /parent/child/grandchild/
child.register_blueprint(grandchild, url_prefix="/grandchild")
parent.register_blueprint(child, url_prefix="/child")
app.register_blueprint(parent, url_prefix="/parent")
The Blueprint.register method handles this logic by stripping trailing slashes from the parent prefix and leading slashes from the child prefix to ensure a clean join:
state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/").
Subdomain Concatenation
Subdomains are also concatenated, but they follow a right-to-left hierarchy typical of DNS. A child's subdomain is prepended to the parent's subdomain.
parent = Blueprint("parent", __name__)
child = Blueprint("child", __name__, subdomain="api")
@child.route("/")
def index():
return "child"
parent.register_blueprint(child)
app.register_blueprint(parent, subdomain="user")
# This route matches: http://api.user.example.test/
Namespacing and Endpoints
When blueprints are nested, their endpoint names are automatically namespaced using a dot (.) notation. This prevents collisions and allows url_for to target specific instances of a blueprint.
If you have a blueprint named child nested under parent, a view function named index in the child blueprint will have the endpoint parent.child.index.
Multiple Registrations
You can register the same blueprint multiple times using different names. This is useful for reusing the same logic under different URL prefixes. The name parameter in register_blueprint (or app.register_blueprint) overrides the default blueprint name for that specific registration.
bp = Blueprint("bp", __name__)
bp2 = Blueprint("bp2", __name__)
@bp2.get("/")
def index2():
return flask.request.endpoint
# Register bp2 under bp with the name 'sub'
bp.register_blueprint(bp2, url_prefix="/a", name="sub")
# Register bp twice on the app
app.register_blueprint(bp, url_prefix="/a")
app.register_blueprint(bp, url_prefix="/b", name="alt")
# Resulting endpoints:
# /a/a/ -> bp.sub.index2
# /b/a/ -> alt.sub.index2
Execution Order and Scoping
The Blueprint._merge_blueprint_funcs method ensures that hooks and error handlers are correctly scoped to the nested hierarchy.
- Request Hooks:
before_requestfunctions are executed from the most general to the most specific (App -> Parent Blueprint -> Child Blueprint). - Error Handlers: When an error occurs, Flask looks for a handler on the blueprint that handled the request. If not found, it moves up the hierarchy to the parent blueprint, and finally to the application.
In tests/test_blueprints.py, a grandchild blueprint can have its own error handler that takes precedence over the parent's handler for routes within that grandchild:
@grandchild.errorhandler(403)
def grandchild_forbidden(e):
return "Grandchild no", 403
@parent.errorhandler(403)
def parent_forbidden(e):
return "Parent no", 403
# A 403 in grandchild_no() returns "Grandchild no"
# A 403 in child_no() returns "Parent no"
Constraints
- No Self-Registration: A blueprint cannot be registered on itself. The
register_blueprintmethod checksif blueprint is selfand raises aValueError. - Naming: Blueprint names cannot contain dots (
.) because the dot is used as the separator for the nested hierarchy. - Registration State: Once a blueprint has been registered (either on an app or another blueprint), you can no longer call setup methods like
recordoradd_url_ruleon it. This is enforced by_check_setup_finished.