# Django 模板中获取 URL:`request.path` 与 `path_info`、`get_full_path`、`build_absolute_uri` 的区别 在 Django 模板中,经常需要获取当前 URL。 * 导航栏“当前菜单激活” * 登录后“返回原来页面(next)” * 生成 canonical URL / 分享链接 * 包含查询字符串的“当前页面不变”链接 然而,除了 `{{ request.path }}`,还有许多类似的属性,容易混淆。今天我们将清晰比较下面四个常用属性。 * `request.path` * `request.path_info` * `request.get_full_path()` * `request.build_absolute_uri()` --- ## `{{ request }}` 是从哪里来的 {#sec-e3874691b2fc} 在模板中看到 `{{ request }}`,通常意味着 **request context processor** 已开启。 * `django.template.context_processors.request` 负责将 `request` 放入模板上下文。 * 该 context processor 在 `TEMPLATES` 设置的 `OPTIONS['context_processors']` 中注册后生效。 换句话说,Django 默认在 `settings.TEMPLATES` 中将 `request` 设为全局上下文,除非开发者刻意移除,否则用户可以无障碍使用。 `request` 对象本身是 Django 在接收请求时创建的 `HttpRequest`(实际上是 WSGI/ASGI 请求子类),并将其传递给视图。该对象包含 `path`、`path_info` 等属性。 ![Django 这个名字的魔术师从帽子里拿出工具的图片](/media/editor_temp/6/c112847e-a9ca-4d58-9a3f-076a20bfbef7.png) --- ## 一句话总结:四个属性的记忆方式 {#sec-0f722c9bd67c} * **`path`**:去掉域名/协议,只保留路径 * **`path_info`**:应用看到的真实路径(去掉脚本前缀) * **`get_full_path()`**:`path` + 查询字符串 * **`build_absolute_uri()`**:完整的 URL(协议+域名+路径) 现在我们逐一深入。 --- ## 1) `request.path`:无域名的“路径(path)” {#sec-26a629a2d24e} `request.path` 只返回 **不含域名/协议** 的请求路径。例如: * `/music/bands/the_beatles/` ### 何时有用 {#sec-b307f735685f} * 简单比较,例如菜单激活 ```django {% if request.path == "/settings/" %}active{% endif %} ``` * 判断“此页面属于哪个分类”,如前缀比较 ```django {% if request.path|slice:":5" == "/api/" %}...{% endif %} ``` --- ## 2) `request.path_info`:在部署环境中更稳的“真实路径” {#sec-fd1e8e14948f} Django 文档的核心要点是: * 某些服务器配置会将 URL 路径拆分为 **脚本前缀(SCRIPT_NAME)** 与 **path info**。 * `path_info` 始终包含 **path info 部分**(即不受环境影响)。 简而言之,在应用挂载在 `/app` 等前缀下(反向代理、子路径部署等)时,`path` 与 `path_info` 可能不同。 ### 何时有用 {#sec-2985f0c9cd7c} * 需要考虑子路径部署(如 `/app/`)时,想用“应用基准路径”判断 * 测试/生产服务器前缀可能不同 > 通常在单域名根路径(`/`)下运行时,`path` 与 `path_info` 相同,难以感知差异;但环境变化后差异才显现。 --- ## 3) `request.get_full_path()`:`path` + 查询字符串 {#sec-1364e4e65388} `get_full_path()` 返回 **`path` 加上查询字符串** 的值。例如: * `/music/bands/the_beatles/?print=true` ### 何时有用 {#sec-0ac89fe1b7cd} * 需要“当前页面不变”的分享/刷新/返回链接 ```django 刷新链接 ``` * 登录/权限检查后返回原页面时的 `next` ```django 登录 ``` > 参考:Django 还有 `get_full_path_info()`,其基于 `path_info`。今天不做比较,但了解差异有益。 --- ## 4) `request.build_absolute_uri()`:包含协议+域名的“完整 URL” {#sec-0ba80b4c4700} `build_absolute_uri()` 根据当前请求生成 **绝对 URL(absolute URI)**。 大致形式: * `https://example.com/music/bands/the_beatles/?print=true` ### 何时有用 {#sec-428d3d6b9613} * 需要域名的链接,例如邮件/聊天分享 * canonical URL、og:url 等元标签 * 向外部系统回调 URL ### 注意事项 {#sec-b24dccc132d6} `build_absolute_uri()` 在构造主机时依赖请求的 Host 信息(内部使用 `get_host()`)。 **这并不一定是实际请求的 URL**。在大多数普通场景下,`get_host()` 的值与实际域名相符,但如果 Nginx 反向代理或 Django 中间件故意使用不同的 tenant host,`{{ request.build_absolute_uri }}` 的值可能与浏览器实际 URL 不一致。 因此,无论是在 nginx 还是 Django 应用内部实现任何域名相关逻辑时,都应检查部署设置,避免产生意外域名/协议。 --- ## 结论与总结 {#sec-bd7ea169a316} * **模板中菜单激活** → `request.path` * **子路径部署/代理环境下稳定比较** → `request.path_info` * **包含查询字符串的“当前页面不变”** → `request.get_full_path()` * **外部完整链接(含域名)** → `request.build_absolute_uri()` --- **相关阅读**: - [什么是反向代理?与正向代理的区别、目的、使用场景一览](/ko/whitedec/2025/12/10/reverse-proxy-forward-proxy-differences/) - [从零开始重新学习 Django:从 HTTP 开始的学习路线图](/ko/whitedec/2025/12/22/django-first-learning-roadmap/)