Skip to content

表单、身份与后台边界 ​

本站纵览 | 练习答案

python
# tickets/forms.py
from django import forms
from .models import Ticket


class TicketForm(forms.ModelForm):
    class Meta:
        model = Ticket
        fields = ["title", "priority"]

    def clean_title(self):
        title = self.cleaned_data["title"].strip()
        if not title:
            raise forms.ValidationError("Title cannot be blank.")
        return title
python
# tickets/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_http_methods
from .forms import TicketForm
from .models import Ticket


@login_required
@require_http_methods(["GET", "POST"])
def ticket_edit(request, pk):
    ticket = get_object_or_404(Ticket, pk=pk, assignee=request.user)
    form = TicketForm(request.POST or None, instance=ticket)
    if request.method == "POST" and form.is_valid():
        form.save()
        return redirect("tickets:detail", pk=ticket.pk)
    return render(request, "tickets/edit.html", {"form": form, "ticket": ticket})
python
# tickets/admin.py
from django.contrib import admin
from .models import Ticket


@admin.register(Ticket)
class TicketAdmin(admin.ModelAdmin):
    list_display = ["title", "status", "priority", "assignee", "updated_at"]
    list_filter = ["status", "priority"]
    search_fields = ["title", "assignee__username"]
    readonly_fields = ["created_at", "updated_at"]
    list_select_related = ["assignee"]

深水边界 ​

  • ModelForm 自动生成字段不等于自动授权;永远显式列出可写字段。
  • login_required 只保证有身份,不保证可操作对象。
  • 把对象归属放入查询条件常能同时避免越权和资源枚举。
  • CSRF 保护基于浏览器携带凭证的威胁模型;不要为“API 化”随意豁免。
  • Admin action 是批量写操作,需要权限、事务、审计和失败反馈。
  • 密码只交给 Django 哈希器,不自行加密或保存明文。

验收 ​

匿名请求被引导登录;越权对象不可编辑;表单忽略未授权字段;普通 staff 的 Admin 查询集不泄露其他人的工单。

练习(先独立完成) ​

  1. 添加工单创建视图,确保提交中的 assignee 被忽略。
  2. 为编辑视图写匿名重定向和越权测试。
  3. 在 Admin 中只让超级用户看到所有工单,普通 staff 只看分配给自己的。

完成后核对答案。

深度加餐:表单把不可信输入变成 cleaned_data ​

表单不是 HTML 的装饰层;它负责绑定、转换、校验和把错误定位到字段。

先做一个可观察实验 ​

python
from django import forms

class TicketForm(forms.Form):
    title = forms.CharField(max_length=120)
    priority = forms.IntegerField(min_value=1, max_value=5)

    def clean_title(self):
        title = self.cleaned_data['title'].strip()
        if title.lower().startswith('admin'):
            raise forms.ValidationError('标题不能以 admin 开头')
        return title

预期:is_valid 后才能读取 cleaned_data;字段错误与非字段错误分开,模板可以准确展示修复提示。

原理下潜与工程边界 ​

Form.clean_fields、clean_字段、clean 的执行顺序决定跨字段校验位置。ModelForm 还会叠加模型约束,但保存 commit=False 时要明确何时补齐外键、何时真正写库。

POST 必须配 CSRF;登录校验不是权限校验;Admin 的 list_display、search_fields、actions 可能触发昂贵查询,生产后台也需要查询预算。

反例排错路线 ​

先检查 request.method 与 form.is_bound,再打印 form.errors.as_data;权限失败时区分 302 登录跳转、403 拒绝和 404 隐藏资源。

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

加码:为工单表单增加“只能修改自己的 OPEN 工单”规则,分别写表单校验和 view 权限测试。

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

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

课程合同:表单、认证与 Admin ​

学习目标 ​

学完本篇,读者应能把「登录用户提交表单,校验失败不写库,staff 在 Admin 中操作」拆成输入、可信边界、Django 机制和可复查证据;同时能说明 Django 6.0 与 5.2 LTS 的差异不会改变业务不变量。

心智模型 ​

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

本地机制图 ​

表单、认证与 Admin机制图

图中的“输入”是不可信的 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 的新能力是否需要条件分支。

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

答案册 ​

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

下一篇 ​

下一篇是05-forms-auth-admin/02-sessions-object-permissions-admin-actions,继续沿同一条输入—边界—机制—证据链前进。

用心记录每一步成长