如果我的方法有多个路由注释,如何使用url_for?

47 投票
3 回答
16959 浏览
提问于 2025-04-17 04:24

我有一个方法可以通过多个路径访问:

@app.route("/canonical/path/")
@app.route("/alternate/path/")
def foo():
    return "hi!"

那么,我该怎么调用 url_for("foo"),才能确保我得到的是第一个路径呢?

3 个回答

1

另外,对于那些使用变量构建的通用路由的用户来说:如果把一个包含变量的字典传给url_for,Flask会正确生成网址路径。

举个例子...

app.py:

app.route('/<path:pattern1>')
app.route('/<path:pattern1>/<path:pattern2>')
def catch_all(pattern1, pattern2=None):
    return render_template('template.html', p1=pattern1, p2=pattern2)

app.route('/test')
def test_routing:
    args = {'pattern1': 'Posts', 'pattern2': 'create'}
    return render_template('test.html', args=args)

test.html:

<a href="{{url_for('catch_all', **args)}}">click here</a>

当你点击“点击这里”的链接时,你会被引导到“Posts/create”这个路由。

51

在Flask中,规则是独特的。如果你把完全相同的URL定义给同一个函数,默认情况下会出现冲突,因为从我们的角度来看,这样做是错误的,我们会阻止你这样做。

有一个原因你可能想要给同一个接口设置多个URL,那就是为了兼容过去的规则。自从WZ0.8和Flask 0.8版本以来,你可以明确为一个路由指定一个别名:

@app.route('/')
@app.route('/index.html', alias=True)
def index():
    return ...

在这种情况下,如果用户请求 /index.html,Flask会自动将请求永久重定向到 /

这并不意味着一个函数不能绑定到多个URL,但在这种情况下,你需要更改端点:

@app.route('/')
def index():
    ...

app.add_url_rule('/index.html', view_func=index, endpoint='alt_index')

或者可以这样做:

@app.route('/')
@app.route('/index.html', endpoint='alt_index')
def index():
    ...

在这种情况下,你可以用不同的名称再次定义一个视图。然而,通常你会想要避免这样做,因为这样视图函数需要检查请求的端点来看看被调用的是哪个。更好的做法是这样:

@app.route('/')
def index():
    return _index(alt=False)

@app.route('/index.html')
def alt_index():
    return _index(alt=True)

def _index(alt):
    ...

在这两种情况下,生成URL的方式是 url_for('index')url_for('alt_index')

你也可以在路由系统层面上这样做:

@app.route('/', defaults={'alt': False})
@app.route('/index.html', defaults={'alt': True})
def index(alt):
    ...

在这种情况下,生成URL的方式是 url_for('index', alt=True)url_for('index', alt=False)

83

好的。我花了一些时间研究了 werkzeug.routingflask.helpers.url_for 的代码,终于搞明白了。其实你只需要改变路由的 endpoint(换句话说,就是给你的路由 命名

@app.route("/canonical/path/", endpoint="foo-canonical")
@app.route("/alternate/path/")
def foo():
    return "hi!"

@app.route("/wheee")
def bar():
    return "canonical path is %s, alternative is %s" % (url_for("foo-canonical"), url_for("foo"))

这样就会产生

标准路径是 /canonical/path/,替代路径是 /alternate/path/

不过,这种方法有一个缺点。Flask 总是把最后定义的路由绑定到隐式定义的 endpoint(在你的代码中就是 foo)。你猜如果重新定义 endpoint 会发生什么?所有的 url_for('old_endpoint') 都会抛出 werkzeug.routing.BuildError 错误。所以,我觉得解决这个问题的正确方法是把标准路径定义在最后,并给替代路径 命名

""" 
   since url_for('foo') will be used for canonical path
   we don't have other options rather then defining an endpoint for
   alternative path, so we can use it with url_for
"""
@app.route('/alternative/path', endpoint='foo-alternative')
""" 
   we dont wanna mess with the endpoint here - 
   we want url_for('foo') to be pointing to the canonical path
"""
@app.route('/canonical/path') 
def foo():
    pass

@app.route('/wheee')
def bar():
    return "canonical path is %s, alternative is %s" % (url_for("foo"), url_for("foo-alternative"))

撰写回答