ModelForm 相比 Form 多做了什么?最危险的一点是什么?这篇看字段是怎么生成的,再用两个 Meta.fields 写法演示批量赋值风险,最后看 instance 在校验前后的状态。
title CharField required=True widget=TextInput body CharField required=False widget=Textarea author ModelChoiceField required=True widget=Select published BooleanField required=False widget=CheckboxInput is_featured BooleanField required=False widget=CheckboxInput slug SlugField required=True widget=TextInput 没出现的模型字段(不可编辑): ['created', 'id']
模型的 editable=False 字段(自增主键、auto_now_add)不会出现。生成的表单字段和 widget 可以在 Meta.widgets / Meta.labels 里覆盖(本文没有测)。
fields="__all__":用户额外提交 is_featured/published → 保存后 is_featured = True published = True fields=[...] 白名单:同样的提交 → is_featured = False published = False (多余字段被忽略)
ModelForm 只处理 fields 列出的字段。用 '__all__' 或 exclude 意味着以后给模型加任何字段,都会自动变成用户可提交的字段。
slug 重复 → is_valid() = False {'slug': [('unique', '%(model_name)s with this %(field_label)s already exists.')]}
title 超长 → {'title': ['max_length']}
slug 格式错 → {'slug': ['invalid']}
author 不存在 → {'author': ['invalid_choice']}这些错误来自三个来源:表单字段(invalid_choice)、模型字段类型与 validators(max_length、invalid)、validate_unique(unique)。
构造后 instance.title = orig is_valid() = False (slug 必填,失败) 此时 instance.title = changed ← 内存里的对象已被改成用户提交的值 库里 = orig 校验通过、还没 save():库里 = orig save() 之后库里 = changed
ModelForm 在 _post_clean() 里把 cleaned_data 写到 instance 上再校验。失败时对象已被改。