diff --git a/CHANGES.rst b/CHANGES.rst index a727f947..0f0b5b76 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -71,6 +71,10 @@ Unreleased - The ``flask run`` command no longer fails if Python is not built with SSL support. Using the ``--cert`` option will show an appropriate error message. :issue:`3211` +- URL matching now occurs after the request context is pushed, rather + than when it's created. This allows custom URL converters to access + the app and request contexts, such as to query a database for an id. + :issue:`3088` .. _#2935: https://github.com/pallets/flask/issues/2935 .. _#2957: https://github.com/pallets/flask/issues/2957 diff --git a/src/flask/ctx.py b/src/flask/ctx.py index c23f7d19..a2944628 100644 --- a/src/flask/ctx.py +++ b/src/flask/ctx.py @@ -314,9 +314,6 @@ class RequestContext(object): # functions. self._after_request_functions = [] - if self.url_adapter is not None: - self.match_request() - @property def g(self): return _app_ctx_stack.top.g @@ -350,8 +347,8 @@ class RequestContext(object): of the request. """ try: - url_rule, self.request.view_args = self.url_adapter.match(return_rule=True) - self.request.url_rule = url_rule + result = self.url_adapter.match(return_rule=True) + self.request.url_rule, self.request.view_args = result except HTTPException as e: self.request.routing_exception = e @@ -395,6 +392,9 @@ class RequestContext(object): if self.session is None: self.session = session_interface.make_null_session(self.app) + if self.url_adapter is not None: + self.match_request() + def pop(self, exc=_sentinel): """Pops the request context and unbinds it by doing that. This will also trigger the execution of functions registered by the diff --git a/tests/test_basic.py b/tests/test_basic.py index 63f23a8d..37e8d5c4 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1384,26 +1384,6 @@ def test_url_for_passes_special_values_to_build_error_handler(app): flask.url_for("/") -def test_custom_converters(app, client): - from werkzeug.routing import BaseConverter - - class ListConverter(BaseConverter): - def to_python(self, value): - return value.split(",") - - def to_url(self, value): - base_to_url = super(ListConverter, self).to_url - return ",".join(base_to_url(x) for x in value) - - app.url_map.converters["list"] = ListConverter - - @app.route("/") - def index(args): - return "|".join(args) - - assert client.get("/1,2,3").data == b"1|2|3" - - def test_static_files(app, client): rv = client.get("/static/index.html") assert rv.status_code == 200 diff --git a/tests/test_converters.py b/tests/test_converters.py new file mode 100644 index 00000000..dd6c4d68 --- /dev/null +++ b/tests/test_converters.py @@ -0,0 +1,40 @@ +from werkzeug.routing import BaseConverter + +from flask import has_request_context +from flask import url_for + + +def test_custom_converters(app, client): + class ListConverter(BaseConverter): + def to_python(self, value): + return value.split(",") + + def to_url(self, value): + base_to_url = super(ListConverter, self).to_url + return ",".join(base_to_url(x) for x in value) + + app.url_map.converters["list"] = ListConverter + + @app.route("/") + def index(args): + return "|".join(args) + + assert client.get("/1,2,3").data == b"1|2|3" + + with app.test_request_context(): + assert url_for("index", args=[4, 5, 6]) == "/4,5,6" + + +def test_context_available(app, client): + class ContextConverter(BaseConverter): + def to_python(self, value): + assert has_request_context() + return value + + app.url_map.converters["ctx"] = ContextConverter + + @app.route("/") + def index(name): + return name + + assert client.get("/admin").data == b"admin" diff --git a/tests/test_testing.py b/tests/test_testing.py index a22f1146..1490840c 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -153,7 +153,9 @@ def test_blueprint_with_subdomain(): ctx = app.test_request_context("/", subdomain="xxx") assert ctx.request.url == "http://xxx.example.com:1234/foo/" - assert ctx.request.blueprint == bp.name + + with ctx: + assert ctx.request.blueprint == bp.name rv = client.get("/", subdomain="xxx") assert rv.data == b"http://xxx.example.com:1234/foo/"