diff --git a/CHANGES.rst b/CHANGES.rst index c2a42443..db4eff41 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,6 +18,8 @@ Unreleased - Show an error when a blueprint name contains a dot. The ``.`` has special meaning, it is used to separate (nested) blueprint names and the endpoint name. :issue:`4041` +- Combine URL prefixes when nesting blueprints that were created with + a ``url_prefix`` value. :issue:`4037` Version 2.0.0 diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py index 823995f6..7bfef84b 100644 --- a/src/flask/blueprints.py +++ b/src/flask/blueprints.py @@ -260,7 +260,7 @@ class Blueprint(Scaffold): """Called by :meth:`Flask.register_blueprint` to register all views and callbacks registered on the blueprint with the application. Creates a :class:`.BlueprintSetupState` and calls - each :meth:`record` callbackwith it. + each :meth:`record` callback with it. :param app: The application this blueprint is being registered with. @@ -344,13 +344,17 @@ class Blueprint(Scaffold): app.cli.add_command(self.cli) for blueprint, bp_options in self._blueprints: - url_prefix = options.get("url_prefix", "") - if "url_prefix" in bp_options: - url_prefix = ( - url_prefix.rstrip("/") + "/" + bp_options["url_prefix"].lstrip("/") + bp_options = bp_options.copy() + bp_url_prefix = bp_options.get("url_prefix") + + if bp_url_prefix is None: + bp_url_prefix = blueprint.url_prefix + + 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("/") ) - bp_options["url_prefix"] = url_prefix bp_options["name_prefix"] = options.get("name_prefix", "") + self.name + "." blueprint.register(app, bp_options) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index cfcb43b3..e7724519 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -835,3 +835,36 @@ def test_nested_blueprint(app, client): assert client.get("/parent/no").data == b"Parent no" assert client.get("/parent/child/no").data == b"Parent no" assert client.get("/parent/child/grandchild/no").data == b"Grandchild no" + + +def test_nested_blueprint_url_prefix(app, client): + parent = flask.Blueprint("parent", __name__, url_prefix="/parent") + child = flask.Blueprint("child", __name__, url_prefix="/child") + grandchild = flask.Blueprint("grandchild", __name__, url_prefix="/grandchild") + apple = flask.Blueprint("apple", __name__, url_prefix="/apple") + + @parent.route("/") + def parent_index(): + return "Parent" + + @child.route("/") + def child_index(): + return "Child" + + @grandchild.route("/") + def grandchild_index(): + return "Grandchild" + + @apple.route("/") + def apple_index(): + return "Apple" + + child.register_blueprint(grandchild) + child.register_blueprint(apple, url_prefix="/orange") # test overwrite + parent.register_blueprint(child) + app.register_blueprint(parent) + + assert client.get("/parent/").data == b"Parent" + assert client.get("/parent/child/").data == b"Child" + assert client.get("/parent/child/grandchild/").data == b"Grandchild" + assert client.get("/parent/child/orange/").data == b"Apple"