form.is_valid() 到底做了什么、按什么顺序?一个字段失败了,后面的钩子还执行吗?这篇给每个环节打点,把完整的校验流程按调用顺序排出来。
构造之后 LOG: []
f.is_bound = True hasattr(f, "cleaned_data") = False
调用 f.errors 之前: errors 是否已计算 = False
is_valid() = True 之后 LOG:
a.to_python('3')
a.validate(3)
a.run_validators(3)
clean_a()
b.to_python('5')
b.validate(5)
b.run_validators(5)
clean_b()
clean()(表单级,cleaned_data 里现有的键:['a', 'b'])
_post_clean()
cleaned_data = {'a': 3, 'b': 5}is_valid() 就是「is_bound 且 errors 为空」;errors 第一次访问时调用 full_clean()。
is_valid() = False
a.to_python('0')
a.validate(0)
a.run_validators(0)
b.to_python('5')
b.validate(5)
b.run_validators(5)
clean_b()
clean()(表单级,cleaned_data 里现有的键:['b'])
_post_clean()
errors: min_value / {'a': ['Ensure this value is greater than or equal to 1.']}
cleaned_data = {'b': 5}字段自己的三个阶段任一失败就不再执行它的 clean_<field>。注意 Django 仍会继续处理其他字段,并且仍会调用表单级 clean()——所以 clean() 必须容忍字段缺失(本站另一篇《Forms 校验流水线》用真实的 KeyError 演示过这一点)。
a > b 时 non_field_errors: ['a 不能大于 b'] code = order
a="x", b="" : {'a': ['invalid'], 'b': ['required']} 顺序 = ['a', 'b']
错误对象: {"a": [{"message": "Enter a whole number.", "code": "invalid"}], "b": [{"message": "This field is required.", "code": "rValidationError 的 code 是给程序判断用的,message 是给人看的;需要给具体字段加错误用 self.add_error('a', ...)(本文没有测)。
未绑定表单 is_valid() = False errors = {}
多余字段 zzz 进入 cleaned_data 吗: False
disabled 字段:提交 99,cleaned_data = {'x': 1} (用 initial,忽略提交的值)
has_changed() = False changed_data = []未绑定表单(没有数据)is_valid() 永远是 False,errors 为空。请求里多余的字段被忽略。disabled=True 的字段用 initial,忽略用户提交的值,是防止篡改只读字段的正确做法(比只在前端 readonly 可靠)。