Merge pull request #4935 from pgjones/subdomain

Fix subdomain inheritance for nested blueprints
This commit is contained in:
David Lord 2023-01-04 14:06:35 -07:00 committed by GitHub
commit 836866dc19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 0 deletions

View file

@ -3,6 +3,8 @@ Version 2.3.0
Unreleased Unreleased
- Ensure subdomains are applied with nested blueprints. :issue:`4834`
Version 2.2.3 Version 2.2.3
------------- -------------

View file

@ -140,6 +140,19 @@ name, and child URLs will be prefixed with the parent's URL prefix.
url_for('parent.child.create') url_for('parent.child.create')
/parent/child/create /parent/child/create
In addition a child blueprint's will gain their parent's subdomain,
with their subdomain as prefix if present i.e.
.. code-block:: python
parent = Blueprint('parent', __name__, subdomain='parent')
child = Blueprint('child', __name__, subdomain='child')
parent.register_blueprint(child)
app.register_blueprint(parent)
url_for('parent.child.create', _external=True)
"child.parent.domain.tld"
Blueprint-specific before request functions, etc. registered with the Blueprint-specific before request functions, etc. registered with the
parent will trigger for the child. If a child does not have an error parent will trigger for the child. If a child does not have an error
handler that can handle a given exception, the parent's will be tried. handler that can handle a given exception, the parent's will be tried.

View file

@ -358,6 +358,9 @@ class Blueprint(Scaffold):
:param options: Keyword arguments forwarded from :param options: Keyword arguments forwarded from
:meth:`~Flask.register_blueprint`. :meth:`~Flask.register_blueprint`.
.. versionchanged:: 2.3
Nested blueprints now correctly apply subdomains.
.. versionchanged:: 2.0.1 .. versionchanged:: 2.0.1
Nested blueprints are registered with their dotted name. Nested blueprints are registered with their dotted name.
This allows different blueprints with the same name to be This allows different blueprints with the same name to be
@ -453,6 +456,17 @@ class Blueprint(Scaffold):
for blueprint, bp_options in self._blueprints: for blueprint, bp_options in self._blueprints:
bp_options = bp_options.copy() bp_options = bp_options.copy()
bp_url_prefix = bp_options.get("url_prefix") bp_url_prefix = bp_options.get("url_prefix")
bp_subdomain = bp_options.get("subdomain")
if bp_subdomain is None:
bp_subdomain = blueprint.subdomain
if state.subdomain is not None and bp_subdomain is not None:
bp_options["subdomain"] = bp_subdomain + "." + state.subdomain
elif bp_subdomain is not None:
bp_options["subdomain"] = bp_subdomain
elif state.subdomain is not None:
bp_options["subdomain"] = state.subdomain
if bp_url_prefix is None: if bp_url_prefix is None:
bp_url_prefix = blueprint.url_prefix bp_url_prefix = blueprint.url_prefix

View file

@ -950,6 +950,55 @@ def test_nesting_url_prefixes(
assert response.status_code == 200 assert response.status_code == 200
def test_nesting_subdomains(app, client) -> None:
subdomain = "api"
parent = flask.Blueprint("parent", __name__)
child = flask.Blueprint("child", __name__)
@child.route("/child/")
def index():
return "child"
parent.register_blueprint(child)
app.register_blueprint(parent, subdomain=subdomain)
client.allow_subdomain_redirects = True
domain_name = "domain.tld"
app.config["SERVER_NAME"] = domain_name
response = client.get("/child/", base_url="http://api." + domain_name)
assert response.status_code == 200
def test_child_and_parent_subdomain(app, client) -> None:
child_subdomain = "api"
parent_subdomain = "parent"
parent = flask.Blueprint("parent", __name__)
child = flask.Blueprint("child", __name__, subdomain=child_subdomain)
@child.route("/")
def index():
return "child"
parent.register_blueprint(child)
app.register_blueprint(parent, subdomain=parent_subdomain)
client.allow_subdomain_redirects = True
domain_name = "domain.tld"
app.config["SERVER_NAME"] = domain_name
response = client.get(
"/", base_url=f"http://{child_subdomain}.{parent_subdomain}.{domain_name}"
)
assert response.status_code == 200
response = client.get("/", base_url=f"http://{parent_subdomain}.{domain_name}")
assert response.status_code == 404
def test_unique_blueprint_names(app, client) -> None: def test_unique_blueprint_names(app, client) -> None:
bp = flask.Blueprint("bp", __name__) bp = flask.Blueprint("bp", __name__)
bp2 = flask.Blueprint("bp", __name__) bp2 = flask.Blueprint("bp", __name__)