表单与数据校验
学习目标
用 ModelForm 建立输入边界,理解绑定表单、is_valid、cleaned_data 和错误信息,知道哪些字段不能让用户提交。
为什么浏览器校验还不够
HTML 的 required、maxlength 能减少误操作,却不能阻止有人直接发请求。服务端必须重新校验。ModelForm 将字段定义、模型约束和错误展示整合起来,避免每个视图重复写类型转换和空值检查。
本章只允许用户编辑标题、分类、正文和状态,不暴露 author、创建时间和 id。将 request.POST 全量赋值给模型,容易出现越权字段覆盖。clean_title 和 clean_body 返回清理后的值;校验失败抛出 ValidationError,表单把错误交给用户修正,而不是写入半成品。
具体怎么做
- 新建
blog/forms.py,明确 fields 白名单。 - 标题去掉首尾空白,至少 5 个字符;正文至少 20 个字符。这里按 Python 字符数计算,不是字节数。
- 新建演示脚本,在 Django shell 中构造一份有错误的提交。
- 当前还没有开放前台写入;第 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