Looking for inverse of url_for in Flask

梦想与她 提交于 2020-07-15 03:03:44

问题


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.


回答1:


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!




回答2:


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')


来源:https://stackoverflow.com/questions/19129407/looking-for-inverse-of-url-for-in-flask

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!