django的modelform重点讲解
什么是 ModelForm
Model 在 Django 对应数据库模型
一个 Model 拥有多个 Model.Field
Form 在 Django 对应表单
一个 Form 拥有多个 Form.Field
ModelForm 即基于 Model 的 Form,把 Model 中的 Field 根据下图中的映射关系自动转化为 Form 中的 Field。
ModelForm 中 Form 的功能
先了解一下ModelForm和Form的关系。
之前学习使用的Form,继承的是BaseForm。我这里用的ModelForm,父类是BaseModelForm,再网上找继承的还是BaseForm。
之前学习Form的时候,讲到Form的2个功能,验证和生成html标签。全部都是在BaseForm这个类里实现的。所以这些功能在ModelForm里一样都有,并且用起来和Form几乎也是一样的。
元类里的参数
model :对应models的哪张表
fields :显示的字段,__all__表示全部字段, fields = "__all__"
exclude :排除的自动
labels :自定义标签名,字典类型 labels = {'username': "名字", 'email': "邮箱", 'user_type': "类型"} 对应上面的例子。如何还在models里设置了verbose_name,还是以这里的labels为准。
help_texts :提示信息,显示在输入框后面。字典类型和上面一样。
widgets :自定义插件,用的还是form的插件,如果直接导入会重名,要加别名 from django.forms import widgets as my_widgets 。用法举例:widgets = {'username': my_widgets.Textarea(attrs={'class': 'c1'})}
error_messages :自定义错误信息,整体错误信息的key是 from django.core.exceptions import NON_FIELD_ERRORS 也就是 '__all__'
field_classes :自定义Form验证的类。默认models里是CharField,那么对于Form的类也是CharField。这个设置可以改掉实现自定义。用法举例:field_classes = {'username': forms.fields.EmailField} 。如果直接导入fields依然会有重名的问题,用as改掉
localized_fields :本地化,根据不同时区显示数据。参数是需要本地化的字段名的元祖,比如:localized_fields=('create_time',)
元类里的很多字段设置都和Form里的用法是一样的。但是Form里是一个字段一个字段设置的,而ModelForm是整张表设置的。所以这里的设置传入的都是字典,key就是字段名,value就是和Form里设置的值一样了
定制 ModelForm
很多情况下自动生成的 ModelForm 并不能满足设计要求,下面我们来讲一下如何定制
定制有两种方式
Meta
使用 Model 转化的时候自定义转化规则
自定义字段
定义额外的 Field,会覆盖 Model 自动生成的 Field
Meta
ModelForm 是通过 Meta 来把 Model.Field 自动转化为 Form.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_messages = { 'invalid' = 'invalid title' } # 自定义 widget # 这里使用了长 80 列,宽 20 行的 textarea widgets = { 'name': Textarea(attrs={'cols': 80, 'rows': 20}), }
Meta 的缺点是不能修改字段的 validators,如果需要自定义 validators,需要在 Meta 外部重新定义一个同名 Field 来覆盖自动生成的 Field
在 Form 中另外定义 Field
这是 Form 中定义 Field 的通用方法,在 ModelForm 中它有两个作用
补充 Model 没有的 Field 到 Form
覆盖 Model 中的同名 Field 定义
且看下面的例子,Article 中已经包含了 title 字段,我们在 ModelForm 中重新定义了它,把 CharField 改为了 ChoiceField,并且自定义了 validators。
覆盖 title 的时候,把 title 从 Meta 中 exclude 掉是可选的,去不去掉的区别在于,你是否需要它为你校验 unique=True 这个数据库级限制。
在这里我们需要校验,因为 ModelForm 校验通过后我需要把它存入数据库,如果这里没有校验的话,碰到同标题的书数据库就会在储存时报错,我们希望把这步校验放在 ModelForm 的校验中,而不是在通过校验后再用 try... except... 来捕获它。
class ArticleForm(forms.ModelForm): title = forms.ChoiceFied(choices=((1, 'alice'), (2, 'bob'),), 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
article = Article.objects.get(pk=1) author = Author.objects.first() form = ArticleForm(request.POST, instance=article, initial={'author': author}) # form 绑定到 article 实例了 # 初始化表单的时候,author 字段的初始值为 author if form.is_valid(): form.save()
instance
给 ModelForm 初始化 Model 实例,后续的操作都作用在这个实例上
initial
给 ModelForm 初始值
如果和 instance 同时被定义,同名 field 的值覆盖 instance 中的值
校验 ModelForm
Form 只会检查内部定义过的 Field,request.POST 中其余 keyword 都会被无视和过滤掉,即不会出现在返回的 cleaned_data 中。
form = ArticleForm(request.POST) # 校验表单 if form.is_valid(): # 保存到数据库 article = form.save()
is_valid() 会调用 full_clean() 来对表单进行全面校验,它又分成三步(定义在基类 Form 中)
根据每个 Field 注册的 validators 做单个 Field 的校验 (比如 title 字段就会校验是否超出最大允许长度 20) 其中在 Field.clean() 执行过后提供了钩子 clean_[field_name],可以自定义该 function 来注册自己的校验方法。
根据 Form 定义的 Field 之间的依赖关系做整个表单的校验,钩子为 clean(),默认为空。
自定义校验通过后的表单处理,钩子为 _post_clean()
这一步中,ModelForm 做了一些额外的检验:如果定义在 Meta 中的 Field 有 unique=True 这个限制,那么 ModelForm 会按照现有数据库中的数据对其校验,看这个 Field 的值是否已存在,如果已存在,则抛出一个 IntegrityError。实际操作中如果强制不校验 unique 的话,可以把该字段从 Meta 中移除,在 ModelForm 中重新定义该字段。
储存 ModelForm 对象
调用 save() 的时候可以传入 commit=False 来避免立即储存,从而通过后续的修改或补充来得到完整的 Model 实例后再储存到数据库。
如果初始化的时候传入了 instance,那么调用 save() 的时候会用 ModelForm 中定义过的字段值覆盖绑定实例的相应字段,并写入数据库。
save() 同样会帮你储存 ManyToManyField,如果 save 时使用了 commit=False,那么 ManyToManyField 的储存需要等该条目存入数据库之后手动调用 ModelForm 的 save_m2m() 方法。
共 0 条评论