【问题标题】:Flask multiple URL handlers process URLs partiallyFlask 多个 URL 处理程序部分处理 URL
【发布时间】:2014-05-07 22:46:43
【问题描述】:

是否可以在烧瓶中定义处理 url 特定部分的 url 路由器并将 url 传递给下一个 url 处理程序进行进一步处理?

用例是 url 中的一个静态部分,它重复很多并且总是需要相同的处理。 /user/1/something

/user/1/something-else

/user/2/...

理想情况下,处理程序将处理/user/<id> 部分(加载数据库记录等)并将结果存储到本地上下文。然后另一个处理程序将处理剩余的 url。这将使我还可以替换 user 部分(例如,使用 /user/<name/ 而无需触及所有其他路由器。

这在烧瓶中是否可行,如果可以,如何实现?

【问题讨论】:

  • 您确定不需要转换器吗?这看起来像是转换器的案例。
  • 什么意思?你能详细说明一下吗?

标签: python url routes flask flask-restful


【解决方案1】:

我不确定你能不能做你想做的事。但是,根据您上面描述的用法,我认为您只需要一个转换器。

什么是转换器?

正是这种魔力让您可以像这样定义路线:

@app.route('/double/<int:number>')
def double(number):
    return '%d' % (number * 2)

以上,路由只接受/double/ints。更好的是,在 url 部分中传递的任何内容都将转换为 int。因此,转换器绰号 :D。

转换器遵循&lt;converter:variable_name&gt; 格式。你可以阅读 Flask 的内置转换器here。

转换器最好的部分是您可以自己编写自定义数据类型!为此,只需从werkzeug.routing.BaseConverter 派生并填写 to_python 和 to_url。下面,我为无处不在的 User 对象创建了一个简单的转换器。

class UserConverter(BaseConverter):
    """Converts Users for flask URLs."""

    def to_python(self, value):
        """Called to convert a `value` to its python equivalent.

        Here, we convert `value` to a User.
        """
        # Anytime an invalid value is provided, raise ValidationError.  Flask
        # will catch this, and continue searching the other routes.  First,
        # check that value is an integer, if not there is no way it could be
        # a user.
        if not value.isdigit():
            raise ValidationError()
        user_id = int(value)

        # Look up the user in the database.
        if user_id not in _database:
            raise ValidationError()

        # Otherwise, return the user.
        return _database[user_id]

    def to_url(self, value):
        """Called to convert a `value` to its `url` equivalent.

        Here we convert `value`, the User object to an integer - the user id.
        """
        return str(value.id)

转换器什么时候被调用?

当烧瓶匹配您的路线并需要填写转换器处理的位置时。因此,给定以下路由 - /find/&lt;user:user&gt;,下面的所有 URL 都由我们的转换器处理。

  1. /find/1
  2. /find/2
  3. /find/jaime
  4. /find/zasdf123

对于上面的 url,UserConverter.to_python 方法是用 '1'、'2'、'jaime' 和 'zasdf123' 调用的。由方法决定这些值是否转换为有效用户。如果是这样,则将有效用户直接传递给路由的user 参数。

但是,flask 仍然需要了解您的新转换器。很简单:

app.url_map.converters['user'] = UserConverter

自定义转换器的最后一个很酷的功能是它们使构建 URL 的过程自然而简单。要创建一个 url,只需:

url_for('find_user', user=user)

最后,一个简单的程序将所有这些联系在一起。

from flask import Flask, url_for
from werkzeug.routing import BaseConverter, ValidationError


class User:

    def __init__(self, id, name):
        self.id = id
        self.name = name


class UserConverter(BaseConverter):
    """Converts Users for flask URLs."""

    def to_python(self, value):
        """Called to convert a `value` to its python equivalent.

        Here, we convert `value` to a User.
        """
        # Anytime an invalid value is provided, raise ValidationError.  Flask
        # will catch this, and continue searching the other routes.  First,
        # check that value is an integer, if not there is no way it could be
        # a user.
        if not value.isdigit():
            raise ValidationError()
        user_id = int(value)

        # Look up the user in the database.
        if user_id not in _database:
            raise ValidationError()

        # Otherwise, return the user.
        return _database[user_id]

    def to_url(self, value):
        """Called to convert a `value` to its `url` equivalent.

        Here we convert `value`, the User object to an integer - the user id.
        """
        return str(value.id)


# Create a `database` of users.
_database = {
    1: User(1, 'Bob'),
    2: User(2, 'Jim'),
    3: User(3, 'Ben')
}

app = Flask(__name__)
app.url_map.converters['user'] = UserConverter

@app.route('/find/<user:user>')
def find_user(user):
    return "User: %s" % user.name

@app.route('/find/<user:user>/extrapath')
def find_userextra(user):
    return 'User extra: %s' % user.name

@app.route('/users')
def list_users():
    # Return some broken HTML showing url's to our users.
    s = ''
    for user in _database.values():
        s += url_for('find_user', user=user) + '<br/>'
    return s

if __name__ == '__main__':
    app.run(debug=True)

如果有不清楚的地方,或者 gasp 存在错误,请告诉我:D。这适用于您的问题,因为您提到了一系列以相同开头的网址--

/user/1/something

/user/1/something-else

/user/2/

对于这些路线,您可以像这样应用自定义转换器:

@app.route('/user/<user:user>/something')

@app.route('/user/<user:user>/something-else')

@app.route('/user/<user:user>/)

【讨论】:

  • 有趣。我不知道这件事。在您的示例中,您能否添加更多路线来演示 @app.route('/find/&lt;user:user&gt;/more') 的使用?我怀疑它就这么简单,但只是想仔细检查一下。
  • 顺便说一句,很好的描述!
  • 我添加了一条额外的路线。真的就这么简单。 @app.route('/find//extrapath') 可能最好的办法是——在任何需要user 的地方,将它推入 url,然后你会自动在路由函数中接收它.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-18
  • 1970-01-01
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
  • 2012-05-23
相关资源
最近更新 更多