bigywater.tech
Django · 64

Form 与 ModelForm 的区别

ModelForm 相比 Form 多做了什么?最危险的一点是什么?这篇看字段是怎么生成的,再用两个 Meta.fields 写法演示批量赋值风险,最后看 instance 在校验前后的状态。

'__all__'
用户额外提交的 is_featured 被保存;白名单写法则被忽略
unique
ModelForm 校验会查库检查 unique,错误 code = unique
instance
校验失败后内存中的 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 里覆盖(本文没有测)。

Meta.fields 与批量赋值

实测 · 本机执行输出
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

实测 · 本机执行输出
构造后 instance.title = orig
is_valid() = False (slug 必填,失败)  此时 instance.title = changed ← 内存里的对象已被改成用户提交的值  库里 = orig
校验通过、还没 save():库里 = orig
save() 之后库里 = changed

ModelForm 在 _post_clean() 里把 cleaned_data 写到 instance 上再校验。失败时对象已被改。

分步演示:ModelForm

ModelForm
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite
    • 没有验证:Meta.exclude 的行为、formfield_callback、对 JSONField / ArrayField 的自动映射、instance 与 commit=False 的组合(第 65 篇)
    京ICP备2026067502号-1京公网安备11010502063487号