Django 从入门到精通:知行博客实战(6.1) · 第 15 / 36 节

表单与数据校验

吴秀峰 3 次阅读 0 条评论 ↓

学习目标

用 ModelForm 建立输入边界,理解绑定表单、is_valid、cleaned_data 和错误信息,知道哪些字段不能让用户提交。

为什么浏览器校验还不够

HTML 的 required、maxlength 能减少误操作,却不能阻止有人直接发请求。服务端必须重新校验。ModelForm 将字段定义、模型约束和错误展示整合起来,避免每个视图重复写类型转换和空值检查。

本章只允许用户编辑标题、分类、正文和状态,不暴露 author、创建时间和 id。将 request.POST 全量赋值给模型,容易出现越权字段覆盖。clean_title 和 clean_body 返回清理后的值;校验失败抛出 ValidationError,表单把错误交给用户修正,而不是写入半成品。

具体怎么做

  1. 新建 blog/forms.py,明确 fields 白名单。
  2. 标题去掉首尾空白,至少 5 个字符;正文至少 20 个字符。这里按 Python 字符数计算,不是字节数。
  3. 新建演示脚本,在 Django shell 中构造一份有错误的提交。
  4. 当前还没有开放前台写入;第 16 章先实现登录,再在第 17 章接入表单,避免出现匿名发布入口。

本章新增或替换的完整文件

以下路径均相对于练习项目根目录。替换同名文件的全部内容;未列出的文件保持上一章版本。

blog/forms.py

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ("title", "category", "body", "status")
        widgets = {"body": forms.Textarea(attrs={"rows": 12})}

    def clean_title(self):
        title = self.cleaned_data["title"].strip()
        if len(title) < 5:
            raise forms.ValidationError("标题至少需要 5 个字符。")
        return title

    def clean_body(self):
        body = self.cleaned_data["body"].strip()
        if len(body) < 20:
            raise forms.ValidationError("请至少输入 20 个字符,说明你的想法。")
        return body

examples/validate_form.py

from blog.forms import PostForm
from blog.models import Category
category = Category.objects.first()
assert category, "先执行 seed_demo"
form = PostForm(data={"title": "短", "body": "太短", "category": category.pk, "status": "published"})
print(form.is_valid())
print(form.errors.as_json(escape_html=True))
assert "title" in form.errors and "body" in form.errors

如何确认成功

python manage.py shell -c "exec(open('examples/validate_form.py').read())"

预期 is_valid 输出 False,错误同时包含 title 和 body。把脚本中标题和正文改长后,验证结果为 True;此时没有调用 save,数据库文章数不会增加。

练习:将 status 改成 administrator,观察 choices 校验;将 category 改成不存在的 id,观察模型选择字段的错误。解释“能解析成整数”与“引用有效分类”为什么是两层不同检查。

常见问题

为什么不要写 request.POST or None? 空 POST 可能变成 None,被误认为未绑定表单。主线用请求方法判断,即使空提交也能产生校验错误。form.save(commit=False) 是不保存所有数据吗? 它返回尚未落库的模型对象,适合补充当前作者;多对多字段还需要保存主对象后调用 save_m2m,本模型当前没有多对多字段。模型约束失败一定能被表单提前发现吗? 并发写入可能在校验之后发生变化,因此数据库约束仍必需,必要时捕获 IntegrityError。

官方参考

把学习连起来

登录后可保存阅读位置、标记章节完成,并在其他设备继续学习。

登录并保存学习进度 →
联系站长

微信:VIPdjango。技术问题建议先在 社区讨论 中交流,方便更多人参与和查阅。

章节讨论 0