Skip to content

Commit cf87ea6

Browse files
committed
fix: correct blueprint name for nested blueprints
Fixes issue #517.
1 parent b1e5d19 commit cf87ea6

File tree

3 files changed

+46
-3
lines changed

3 files changed

+46
-3
lines changed

doc/scaling.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ Use With Blueprints
152152
-------------------
153153

154154
See :doc:`flask:blueprints` in the Flask documentation for what blueprints are and why you should use them.
155-
Here's an example of how to link an :class:`Api` up to a :class:`~flask.Blueprint`. Nested Blueprints are
156-
not supported.
155+
Here's an example of how to link an :class:`Api` up to a :class:`~flask.Blueprint`.
157156

158157
.. code-block:: python
159158

flask_restx/api.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def __init__(
183183
self.resources = []
184184
self.app = None
185185
self.blueprint = None
186+
self._blueprint_name = None
186187
# must come after self.app initialisation to prevent __getattr__ recursion
187188
# in self._configure_namespace_logger
188189
self.default_namespace = self.namespace(
@@ -525,10 +526,29 @@ def namespace(self, *args, **kwargs):
525526

526527
def endpoint(self, name):
527528
if self.blueprint:
528-
return "{0}.{1}".format(self.blueprint.name, name)
529+
if self._blueprint_name is None:
530+
self._blueprint_name = self._get_blueprint_name()
531+
return "{0}.{1}".format(self._blueprint_name, name)
529532
else:
530533
return name
531534

535+
def _get_blueprint_name(self):
536+
"""
537+
Get full blueprint name from the current_app.blueprints dict,
538+
which contains full names for nested blueprints.
539+
540+
:rtype: str
541+
"""
542+
return next(
543+
(
544+
name
545+
for name, bp in current_app.blueprints.items()
546+
if bp == self.blueprint
547+
),
548+
# Fallback option when blueprint is not yet registered to the app.
549+
self.blueprint.name,
550+
)
551+
532552
@property
533553
def specs_url(self):
534554
"""

tests/test_api.py

+24
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ def test_root_endpoint_with_blueprint_with_subdomain(self, app):
4343
assert url == "http://api.localhost/api/"
4444
assert api.base_url == "http://api.localhost/api/"
4545

46+
def test_root_endpoint_with_nested_blueprint(self, app):
47+
blueprint = Blueprint("api", __name__, url_prefix="/api")
48+
blueprint2 = Blueprint("v1", __name__, url_prefix="/v1")
49+
blueprint.register_blueprint(blueprint2)
50+
api = restx.Api(blueprint2, version="1.0")
51+
app.register_blueprint(blueprint)
52+
53+
with app.test_request_context():
54+
url = url_for("api.v1.root")
55+
assert url == "/api/v1/"
56+
assert api.base_url == "http://localhost/api/v1/"
57+
58+
def test_root_endpoint_with_nested_blueprint_with_subdomain(self, app):
59+
blueprint = Blueprint("api", __name__, subdomain="api", url_prefix="/api")
60+
blueprint2 = Blueprint("v1", __name__, url_prefix="/v1")
61+
blueprint.register_blueprint(blueprint2)
62+
api = restx.Api(blueprint2, version="1.0")
63+
app.register_blueprint(blueprint)
64+
65+
with app.test_request_context():
66+
url = url_for("api.v1.root")
67+
assert url == "http://api.localhost/api/v1/"
68+
assert api.base_url == "http://api.localhost/api/v1/"
69+
4670
def test_parser(self):
4771
api = restx.Api()
4872
assert isinstance(api.parser(), restx.reqparse.RequestParser)

0 commit comments

Comments
 (0)