HOWTO · Flask

在 Flask 中使用 url_for 创建动态 URL

了解 Flask 如何通过 url_for() 根据视图端点生成 URL、填入动态路由值、执行重定向,并在 Jinja 模板中创建链接。

本页内容

在 Flask 中,url_for() 根据端点生成 URL,端点通常是视图函数的名称。将路由值作为关键字参数传入,即可填充动态路径。该函数返回 URL 字符串;若要让浏览器跳转到该 URL,应在视图中返回 redirect() 响应。

url_for() 如何构建 URL

Flask 会为每个视图注册一个端点。默认情况下,端点就是视图函数的名称,因此 url_for("index") 指向名为 index 的视图。Flask 会查找该端点对应的路由并生成 URL。这样,链接就能与路由定义保持关联,无需在应用中到处重复写路径字符串。

生成的结果是 / 或 /users/Harry 这样的路径。如果路由包含可变部分,请使用名称匹配的关键字参数传入对应值。不属于路由的其他值会作为查询参数添加。生成 URL 不会发起请求,也不会让访客跳转。

根据视图函数构建 URL

下面这个小型应用定义了主页视图、个人资料视图、用于重定向的视图,以及一个 Jinja 页面。它的测试客户端会请求 Jinja 页面并检查重定向响应,无需启动服务器。运行前,请先在 Python 环境中安装 Flask。在提供的运行环境中,测试客户端渲染出了链接,并收到状态码为 302 的响应,其 Location 标头为 /users/Harry。

运行以下示例:

from flask import Flask, redirect, render_template_string, url_for

app = Flask(__name__)


@app.get("/")
def index():
    return "Home"


@app.get("/users/<string:user_name>")
def profile(user_name):
    return f"Profile: {user_name}"


@app.get("/go")
def go():
    return redirect(url_for("profile", user_name="Harry"))


@app.get("/links")
def links():
    return render_template_string(
        '<a href="{{ url_for(\'profile\', user_name=\'Josh\') }}">Profile</a>'
    )


with app.test_client() as client:
    page = client.get("/links")
    print(page.get_data(as_text=True))
    response = client.get("/go", follow_redirects=False)
    print(response.status_code)
    print(response.headers["Location"])
<a href="/users/Josh">Profile</a>
302
/users/Harry

端点参数是视图函数名 profile。Flask 处理 /go 请求时,url_for() 会生成个人资料页面的路径,而 redirect() 会返回指向该路径的重定向响应。测试客户端会先打印渲染出的链接,再打印重定向状态码及其 Location 标头。渲染出的链接文字 Profile 保持为英文。使用同一个应用时,url_for("index") 会生成主页路径。

传递动态路由值

路由 /users/<string:user_name> 声明了一个名为 user_name 的变量;调用 url_for("profile", user_name="Harry") 时,会为这个变量传入对应的值。Flask 会将该值插入路径中,并在需要时进行 URL 编码。端点名称和各路由变量名必须与视图及关键字参数一致,否则 URL 构建时无法匹配该路由。

对于 /articles/<int:article_id> 这样的路由,可以通过 url_for("article", article_id=42) 传入整数。路由变量未使用的值(例如 sort="recent")会成为查询参数。路径结构使用路由变量,查询数据则使用额外的关键字参数。

使用生成的 URL 重定向

在视图中,返回 redirect(url_for("profile", user_name="Harry"))。url_for() 提供目标路径,redirect() 则创建 HTTP 响应,告知浏览器跳转到该路径。示例中的 /go 视图使用了这种写法。如果只返回 url_for(...),响应正文中只会包含 URL 字符串,浏览器并不会因此跳转。

路由发生变化时,这一区别很有用:重定向仍会指向端点当前注册的路由,因此视图无需写死路径。请根据需要选择重定向行为,因为重定向是对当前请求的响应,浏览器随后会单独请求目标地址。

在 Jinja 模板中生成链接

在 Jinja 模板中,可以在锚点的 href 中使用相同的端点和路由值:

<a href="{{ url_for('profile', user_name='Josh') }}">Profile</a>

这里的 profile 与视图名称一致,user_name 则填入该路由必需的路径变量。可运行示例会在 /links 处渲染这个模板字符串,因此生成的链接指向个人资料路由。对于存储在文件中的模板,可以通过 Flask 的 render_template("index.html") 返回页面;该文件应放在应用的 templates 目录中。不要传入目标端点未声明的路由值,并确保端点拼写与应用中的定义完全一致。