I am using Flask and Flask-RESTful to build a REST API. Within this API some of my resources contain url relations to other resources.
When performing POST requests to these resources I am finding that I am needing the inverse of Flask's url_for() function to parse the incoming url.
For example, a POST to https://www.example.com/buildings
may contain the following json:
{
"address": "123 Lyall St",
...
"owner": {
"href": "https://www.example.com/users/21414512"
},
"tenant": {
"href": "https://www.example.com/users/16324642"
},
}
I would like to parse the id out of owner
and tenant
using the following route:
"https://www.example.com/users/<int:id>"
Is there a convenient way to do this within Flask or Werkzueg or should I just parse the url myself? It would be nice to be able to re-use the already defined route...
I found this post but it does not seem to describe how to do it outside of a request.
I use the route_from
function below:
from flask.globals import _app_ctx_stack, _request_ctx_stack
from werkzeug.urls import url_parse
def route_from(url, method = None):
appctx = _app_ctx_stack.top
reqctx = _request_ctx_stack.top
if appctx is None:
raise RuntimeError('Attempted to match a URL without the '
'application context being pushed. This has to be '
'executed when application context is available.')
if reqctx is not None:
url_adapter = reqctx.url_adapter
else:
url_adapter = appctx.url_adapter
if url_adapter is None:
raise RuntimeError('Application was not able to create a URL '
'adapter for request independent URL matching. '
'You might be able to fix this by setting '
'the SERVER_NAME config variable.')
parsed_url = url_parse(url)
if parsed_url.netloc is not "" and parsed_url.netloc != url_adapter.server_name:
raise NotFound()
return url_adapter.match(parsed_url.path, method)
I wrote this by looking at the implementation of url_for
and reversing it.
The url
argument can be a complete URL or just the path info portion. The return value is a tuple with the endpoint name and a dict
with the arguments.
Disclaimer: I haven't tested it extensively. I was planning to eventually submit it as a pull request, but never seem to get around to fully test it and write some unit tests. If it does not work for you, let me know!
The simplest way create test request context (thanks Leon Young):
with app.test_request_context(YOUR_URL) as request_ctx:
url_rule = request_ctx.request.url_rule
But all sense under the hood of creating request context:
from flask.testing import make_test_environ_builder
builder = make_test_environ_builder(app, YOUR_URL)
environ = builder.get_environ()
url_adapter = app.url_map.bind_to_environ(environ)
url_rule, view_args = url_adapter.match(return_rule=True)
If no reason check protocol and host you can create special match method:
from functools import partial
url_adapter = app.url_map.bind('localhost')
match = partial(url_adapter.match, return_rule=True)
And use it without protocol and host:
owner_url_rule, owner_view_args = match('/users/21414512')
tenant_url_rule, tenant_view_args = match('/users/16324642')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With