Skip to main content

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.

  1. Request Hooks: before_request functions are executed from the most general to the most specific (App -> Parent Blueprint -> Child Blueprint).
  2. 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_blueprint method checks if blueprint is self and raises a ValueError.
  • 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 record or add_url_rule on it. This is enforced by _check_setup_finished.