Skip to content

URL、视图与模板边界 ​

本站纵览 | 练习答案

先用临时数据跑通请求;第三站再替换成模型。

python
# tickets/urls.py
from django.urls import path
from . import views

app_name = "tickets"
urlpatterns = [
    path("", views.ticket_list, name="list"),
    path("<int:pk>/", views.ticket_detail, name="detail"),
]
python
# tickets/views.py
from django.http import Http404
from django.shortcuts import render

TICKETS = {1: {"id": 1, "title": "Printer offline", "status": "open"}}


def ticket_list(request):
    return render(request, "tickets/list.html", {"tickets": TICKETS.values()})


def ticket_detail(request, pk):
    ticket = TICKETS.get(pk)
    if ticket is None:
        raise Http404("ticket not found")
    return render(request, "tickets/detail.html", {"ticket": ticket})
python
# config/urls.py(加入)
from django.urls import include, path

urlpatterns += [path("tickets/", include("tickets.urls"))]
html
<!-- tickets/templates/tickets/list.html -->
<h1>Tickets</h1>
<ul>
  {% for ticket in tickets %}
    <li><a href="{% url 'tickets:detail' ticket.id %}">{{ ticket.title }}</a></li>
  {% empty %}
    <li>No tickets yet.</li>
  {% endfor %}
</ul>

深水边界 ​

  • URL 是外部合同。路径转换器只做结构匹配,业务权限留在视图/服务。
  • Http404 隐藏“对象不存在”和“对象不可见”的差别,可减少枚举风险。
  • 模板自动转义是默认护栏;富文本必须在可信边界净化。
  • 类视图的 MRO 和 mixin 顺序会改变行为;不理解生命周期时,短函数更安全。
  • 流式响应、文件下载和异步视图有不同资源生命周期,不要用普通模板经验类推。

验收 ​

命名路由可以反向解析;列表、详情、空状态和 404 均有测试;模板未使用来源不明的 safe 输出。

练习(无答案正文) ​

  1. 给列表加 ?status=open 过滤,并拒绝未知状态。
  2. 创建基础模板,使用 block 让列表与详情复用标题和导航。
  3. 为详情页写 200 与 404 测试。

完成后查看答案。

深度加餐:URL 是输入校验的第一道门 ​

路径参数先被转换器解析,再进入 view;view 不应重新猜测字符串的含义,也不应把模板当数据库。

先做一个可观察实验 ​

python
# tickets/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('tickets/<int:ticket_id>/', views.ticket_detail, name='ticket-detail'),
]

# tickets/views.py
from django.shortcuts import get_object_or_404, render
from .models import Ticket

def ticket_detail(request, ticket_id):
    ticket = get_object_or_404(Ticket.objects.select_related('owner'), pk=ticket_id)
    return render(request, 'tickets/detail.html', {'ticket': ticket})

预期:非整数路径不会进入 view;不存在的工单得到 404,而不是把 DoesNotExist 泄漏成 500。

原理下潜与工程边界 ​

URL name 是反向解析的稳定合同,模板和重定向都应使用 reverse/get_absolute_url。View 负责协调输入、业务和响应;模板负责展示,不应在循环里触发隐式查询。

path converter 只做形状解析,不代表对象存在;GET view 不应修改状态;redirect 使用 302/303 的语义要和表单提交后的刷新策略匹配。

反例排错路线 ​

先用 resolve('/tickets/1/') 查看命名路由,再用 django.test.Client 检查状态码、模板和 context;模板 N+1 要回到 queryset,而不是在模板加 if。

练习加码(仍不提供答案) ​

加码:为 detail view 添加登录与 owner 权限,写三个测试:合法、不存在、无权访问,并确保 URL 名称不变。

验收时请补充:正常路径、权限或失败路径、以及一条能证明事务/资源/配置边界没有被绕过的测试。

继续查阅:Django 官方文档(外部资料已收录于证据账本) · Django 安全清单(外部资料已收录于证据账本)

课程合同:URL、View 与 Template ​

学习目标 ​

学完本篇,读者应能把「工单列表、详情、404 与 405 由命名 URL 组成稳定合同」拆成输入、可信边界、Django 机制和可复查证据;同时能说明 Django 6.0 与 5.2 LTS 的差异不会改变业务不变量。

心智模型 ​

请求/数据输入 → 边界检查 → Django 核心机制 → 可观察输出 → 恢复动作。配置、数据库提交和外部副作用分别拥有自己的生命周期;没有证据的“应该可以”不算完成。

本地机制图 ​

URL、View 与 Template机制图

图中的“输入”是不可信的 HTTP、表单、任务或配置;“边界”负责身份、格式、权限、大小、超时与版本;“机制”才是 URL、View、ORM、事务、缓存、Tasks 或 ASGI;“证据”是测试、响应、日志、查询计划和发布记录。

最小实验 ​

在全新虚拟环境中运行:

bash
python -m django --version
python manage.py check
python manage.py test

实验只验证一个机制:输入一条有效值,再输入一条空值、重复值或无权限值。每次实验都记录 Django 版本、数据库、命令和退出码。

完整场景 ​

把实验接到 tickets 工单流:请求携带用户身份和工单 ID,经过当前专题负责的边界,完成一次读或写,并留下响应状态、数据库变化、日志字段和清理动作。完整场景必须能第二次执行,或明确说明幂等键、事务回滚、缓存失效和任务重试策略。

运行输出 ​

成功路径应出现:

text
Django 6.0.x
System check identified no issues (0 silenced).
OK

失败路径要保留真实信号,例如 400(输入无效)、403(权限/CSRF)、404(资源不存在)、405(方法不允许)或数据库约束异常;不要把失败改写成含糊的 200。

失败边界 ​

至少验证:空输入、重复提交、匿名用户、越权对象、并发写入、下游不可用、超时和版本不兼容。记录“谁拒绝、何时拒绝、状态是否回滚、重试是否安全”。开发模式的 traceback 不得进入生产响应,缓存不得跨用户复用敏感结果。

原理与排错 ​

先确认版本和 settings,再用 manage.py check 定位启动问题;接着确认 URL/任务入口、输入解析和权限;最后检查 SQL、事务提交、响应和副作用。若结果异常,用最小复现打开 SQL/请求 ID/任务 ID 观测,沿「复现 → 缩小 → 观测 → 修复 → 回归」闭环,不靠重装依赖猜测。

练习(正文不泄答案) ​

  1. 为上述完整场景补一条正常路径和两条失败路径测试,并记录完整输出。
  2. 画出本篇机制图,标出身份 owner、数据提交点、缓存/任务副作用和恢复动作。
  3. 故意制造一次重复、越权或超时,说明应返回的状态、日志字段和是否可重试。
  4. 写一条 Django 5.2 LTS 兼容验收标准,再说明 Django 6.0 的新能力是否需要条件分支。

验收标准:命令可重复、输出可复查、正文没有练习答案;完成后再打开本阶段答案册。

答案册 ​

本阶段答案册提供参考实现、关键推理、失败边界和替代方案;先独立完成练习再对照。

下一篇 ​

下一篇是02-urls-views-templates/02-class-views-static-media,继续沿同一条输入—边界—机制—证据链前进。

用心记录每一步成长