什么是 ModelForm
ModelForm 是 Django 中编写基于 Model 定制表单的方法,可以提高 Model 复用性。
使用时 Django 会根据 django.db.models.Field
(用于数据库衔接) 自动转化为 django.forms.Field
(用于表单前端展示、后端验证)。
定义 ModelForm 表单
举一个书籍管理例子,这个例子中定义了
- title 字段,存 char,用于储存书籍的标题
- 最大允许长度 20
- 数据库唯一值,这意味着数据库不能储存两个相同标题的书
- author 字段,存 Author 的外键,指向储存书籍的作者
Model
class Article(models.Model):
title = models.CharField(max_length=20, unique=True)
author = models.ForeignKey('Author')
ModelForm
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
用 Form 来构造的话这个 ModelForm 类似这样
class ArticleForm(forms.Form):
title = forms.CharField(max_length=20)
author = forms.ModelChoiceField(queryset=Author.objects.all())
定制 ModelForm 表单
ModelForm 提供了自定义 Field、错误信息、渲染方法、正反选择模型的字段等。
如何定制 ModelForm 呢?有两种方式:Meta (ModelForm 独有) 和自定义字段 (普通 Form 中也可使用)。
Meta
ModelForm 通过 Meta 把 db.Field 自动转化为 forms.Field,其中涉及到几步转化
- validators 不变
- 添加 widget 属性,即前端的渲染方式
- 修改 Model 包含的字段,通过 fields 来拿指定字段或者通过 exclude 来排除指定字段
- 修改错误信息
我们可以通过下面的例子来看一看如何通过 Meta 来自定义 ModelForm 的
class ArticleForm(forms.ModelForm):
class Meta:
# 指定 Model
model = Article
# Form 需要 Model 中的哪几个 Field
fields = ['title']
# Form 排除 Model 中的哪几个 Field
exclude = ['author']
# 自定义 error_message
error_messages = {
'invalid' = 'invalid title'
}
# 自定义 widget,这里使用了长 80 列,宽 20 行的 textarea
widgets = {
'name': Textarea(attrs={'cols': 80, 'rows': 20}),
}
Meta 的缺点是不能修改字段的 validators,如果需要修改 validators,需要在 Meta 外部重新定义一个同名 Field 来覆盖之
在 Form 中另外定义 Field
这是 Form 中通用的定义 Field 的方法,在 ModelForm 中它有两个作用
- 补充 Model 没有的 Field 到表单
- 覆盖 Model 中的 Field 定义
且看下面的例子,Article 中已经包含了 title 字段,我们在 ModelForm 中重新定义了它,把 CharField 改为了 ChoiceField,并且自定义了 validators。
注意:覆盖 title 的时候,把 title 从 Meta 中 exclude 掉是可选的,去不去掉的区别在于,你是否需要它为你校验 unique=True
这个数据库级限制。在这里我们需要校验,因为 ModelForm 校验通过后我需要把它存入数据库,如果这里没有校验的话,碰到同标题的书数据库就会在储存时报错,我们希望把这步校验放在 ModelForm 的校验中,而不是在通过校验后再用 try... catch...
来捕获它。
class ArticleForm(forms.ModelForm):
title = forms.ChoiceFied(choices=((1, '罗宾汉'), (2, '田伯光'),), validators=MaxValueValidator(2))
class Meta:
model = Article
值得一提的一些 Field 转化
AutoField
该 Field 不会出现在 ModelForm 表单中。
所有
editable=False
的 Field 都不会出现在 ModelForm 中。
BooleanField
由于表单提交时统一识别为 string,而 BooleanField
是用 python 中的 bool
来判断的,所以只要传了任意非空值,BooleanField
都会当做 True 来处理,而如果传了空值,由于 forms.Field
默认属性是 required=True
,会校验失败,所以如果你需要一个可以填 False 的 Field,那么你需要在 Form
中手动设置这个 Field 的 required=False
。
ForeignKey
ForeignKey 自动转化为 ModelChoiceField,用下拉选项菜单渲染,默认渲染出来的选项显示为对应 Field 的 str
,提交的值为对应 Field 的 id
,这些都可以定制。
在后端接收提交的时候会自动在对应的 Model 中用 id 去找,如果没找到则抛出 ValidationError。
ManyToManyField
ManyToManyField 自动转化为 ModelMultipleChoiceField,用多选框渲染,同样默认渲染出来的选项显示为对应 Field 的 str
,提交的值为对应 Field 的 id
值。
比如有个叫 group 的 ManyToManyField,选中了 'finance'
'develop'
这两个选项,他们的 id 分别为 1 和 2,那么世界上提交的表单 QueryString 就是 group=1&group=2
初始化 ModelForm 表单
form = ArticleForm(request.POST)
-
校验的时候可以定义 instance 参数来给 ModelForm 初始化实例,即后续的修改都作用在这个实例上
-
可以定义 initial 参数来给 ModelForm 初始值,同名 Field 会覆盖 instance 的值
article = Article.objects.get(pk=1)
author = Author.objects.first()
form = ArticleForm(request.POST, instance=article, initial={‘author’: author})