Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 517 #543

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions doc/scaling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ Use With Blueprints
-------------------

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

.. code-block:: python

Expand Down
22 changes: 21 additions & 1 deletion flask_restx/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ def __init__(
self.resources = []
self.app = None
self.blueprint = None
self._blueprint_name = None
# must come after self.app initialisation to prevent __getattr__ recursion
# in self._configure_namespace_logger
self.default_namespace = self.namespace(
Expand Down Expand Up @@ -525,10 +526,29 @@ def namespace(self, *args, **kwargs):

def endpoint(self, name):
if self.blueprint:
return "{0}.{1}".format(self.blueprint.name, name)
if self._blueprint_name is None:
self._blueprint_name = self._get_blueprint_name()
return "{0}.{1}".format(self._blueprint_name, name)
else:
return name

def _get_blueprint_name(self):
"""
Get full blueprint name from the current_app.blueprints dict,
which contains full names for nested blueprints.

:rtype: str
"""
return next(
(
name
for name, bp in current_app.blueprints.items()
if bp == self.blueprint
),
# Fallback option when blueprint is not yet registered to the app.
self.blueprint.name,
)

@property
def specs_url(self):
"""
Expand Down
24 changes: 24 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ def test_root_endpoint_with_blueprint_with_subdomain(self, app):
assert url == "http://api.localhost/api/"
assert api.base_url == "http://api.localhost/api/"

def test_root_endpoint_with_nested_blueprint(self, app):
blueprint = Blueprint("api", __name__, url_prefix="/api")
blueprint2 = Blueprint("v1", __name__, url_prefix="/v1")
blueprint.register_blueprint(blueprint2)
api = restx.Api(blueprint2, version="1.0")
app.register_blueprint(blueprint)

with app.test_request_context():
url = url_for("api.v1.root")
assert url == "/api/v1/"
assert api.base_url == "http://localhost/api/v1/"

def test_root_endpoint_with_nested_blueprint_with_subdomain(self, app):
blueprint = Blueprint("api", __name__, subdomain="api", url_prefix="/api")
blueprint2 = Blueprint("v1", __name__, url_prefix="/v1")
blueprint.register_blueprint(blueprint2)
api = restx.Api(blueprint2, version="1.0")
app.register_blueprint(blueprint)

with app.test_request_context():
url = url_for("api.v1.root")
assert url == "http://api.localhost/api/v1/"
assert api.base_url == "http://api.localhost/api/v1/"

def test_parser(self):
api = restx.Api()
assert isinstance(api.parser(), restx.reqparse.RequestParser)
Expand Down