form与modelform组件
一.Django的forms组件应用场景
1.应用
- 一般应用于前端的登入、注册界面, 对用户输入的字段进行校验, 快速的判断用户输入的内容是否合法, 并返回信息
2.为何不在前端直接使用JS进行校验
- 前端的校验可以没有, 但后端的校验必须要有
- 因为前端的校验弱不禁风, 有很多种方式可以伪装成浏览器发送请求传递数据
- 或者通过爬虫程序绕过前端页面直接朝后端提交数据
二.Forms组件使用步骤
-
导入forms组件
from django import forms
-
定义一个类, 并继承Form,在类中书写要校验的字段, 字段的属性就是要校验的规则
class MyForm(forms.Form): username = forms.CharField(min_length=3, max_length=8) # username字段最少三个字符最大八个字符 age = forms.IntegerField(min_value=0, max_value=200) # 年龄最小0 最大200 email = forms.EmailField() # 必须符合邮箱格式
-
实例化得到一个Form对象, 把要校验的数据传入
form_obj = views.MyForm({'username':'jason','age':18,'email':'123'})
forms组件校验方法:
1. form_obj.is_valid() # 判断数据是否全部符合要求 False(只要有一个不符合结果都是False) 2. form_obj.cleaned_data # 获取符合校验条件的数据 {'username': 'jason', 'age': 18} 3. form_obj.errors # 获取不符合校验规则的数据及原因 {'email': ['Enter a valid email address.']}
三.forms组件基本使用示例
简单的用户注册界面示例
1.常用字段属性
属性 | 释义 |
min_length | 最小长度 |
max_length | 最大长度 |
label | 该字段的标签 |
error_messages | 错误提示 |
validators | 正则校验器 |
initial | input框里面的初始值 |
required | 是否必填 |
2.组件文件 form.py
- 建议在应用文件夹下创建一个专门存放forms组件的文件 (不创建直接写在views.py中也可以)
- 导入组件并创建类
from django import forms class RegisterForm(forms.Form): # 校验name字段,最大长度为8,最短为3,下面的字段类似 name = forms.CharField(max_length=8, min_length=3, lable='用户名') password = forms.CharField(max_length=10, min_length=4, lable='密码') re_password = forms.CharField(max_length=10, min_length=4, lable='确认密码') email = forms.EmailField(lable='邮箱')
3.视图层 views.py 文件
- 实例forms对象, 使用对方法进行校验
from app01.myform import RegisterForm def register(request): if request.method == 'POST': # 实例得到form对象,将需要校验的数据传入(数据可以是前端传过来的数据,也可以是自己后端已有的数据) # dic = {'name':'alex','password':'1111','re_password':'1111','email':'123@qq.com'} # form_data = RegisterForm(dic) # 示例:直接将已有的数据传入 form_data = RegisterForm(request.POST) # 将前端post请求的数据传入 # 进行校验 is_valid() if form_data.is_valid(): print('校验成功') # 获得校验成功的数据 cleaned_data print(form_data.cleaned_data) return HttpResponse('校验成功') else: print('检验失败') # 获得检验失败的错误提示 errors print(form_data.errors) # forms组件可以自动生成HTML代码,因此可以直接在页面上进行显示 form_data = RegisterForm() # 先产生form空对象,再将其传给前端 return render(request, 're_form.html', {'form': form_data})
ps : cleaned_data
属性必须是经过is_valid( )
校验之后才产生的, 在校验之前使用会报错 : 属性不存在
4.路由层 urls.py 文件
path('re_form/', views.register)
5.模板层 re_form.html 文件
<div class="container"> <div class="row"> <div class="col-md-4"> <div class="panel panel-info"> <div class="panel-heading">注册页面</div> <div class="panel-body"> <form action="" method="post"> <p>用户名:{{ form.name }}</p> <p>密码:{{ form.password }}</p> <p>确认密码:{{ form.re_password }}</p> <p>邮箱:{{ form.email }}</p> <br> <input type="submit" class="btn btn-block btn-success" value="注册"> </form> </div> </div> </div> </div> </div>
四.forms组件之渲染标签方式
1.自动渲染方式一
- 就是上面示例中的写法
- 可扩展性很强, 但是需要书写的代码太多
<form action="" method="post"> <p>用户名:{{ form.name }}</p> <p>密码:{{ form.password }}</p> <p>确认密码:{{ form.re_password }}</p> <p>邮箱:{{ form.email }}</p> <br> <input type="submit" class="btn btn-block btn-success" value="注册"> </form>
- 也可以使用label标签替代前缀(’‘用户名’’、’‘密码’'等等)
<form action="" method="post"> <p>{{ form.name.label }}:{{ form.name }}</p> <p>{{ form.password.label }}:{{ form.password }}</p> <p>{{ form.re_password.label }}:{{ form.re_password }}</p> <p>{{ form.email.label }}:{{ form.email }}</p> <br> <input type="submit" class="btn btn-block btn-success" value="注册"> </form>
2.自动渲染方式二(常用方式)
- 使用for循环
- 代码书写简单, 并且扩展性也高 (推荐使用)
<form action="" method="post" novalidate> {% for foo in form %} <div class="form-group"> {# 循环取出 name,password,re_password,email #} <p>{{ foo.label }}:{{ foo }}</p> </div> {% endfor %} <input type="submit" class="btn btn-block btn-success" value="注册"> </form>
3.自动渲染方式三(不常用)
- 代码书写极少, 封装程度太高, 不便于后续的扩展, 一般情况下只在本地测试使用
<form action="" method="post" novalidate> {{ form.as_p }} {# 与上两种显示方式相同 #} {{ form.as_ul }} {# 前面带点 #} {{ form.as_table }} {# 在一行显示 #} <input type="submit" class="btn btn-block btn-success" value="注册"> </form>
注意事项:
- forms组件只负责渲染获取用户数据的标签,也就意味着form标签与按钮都需要自己写;
- 前端的校验是弱不禁风的,最终都需要后端来校验,所以我们在使用forms组件的时候可以直接取消前端帮我们的校验,设置参数 <form action=" " novalidate>
五.forms组件之渲染错误信息
1.字段参数(举例)
-
required : 是否允许为空,默认False
-
error_messages : 自定义错误信息
-
widget
: 使用HTML插件
- TextInput
- PasswordInput
2.代码演示
- myform.py 文件
from django import forms from django.forms import widgets class RegisterForm(forms.Form): # 校验name字段,最大长度为8,最短为3,下面的字段类似 name = forms.CharField(max_length=8, min_length=3, label='用户名', # 自定义错误信息 error_messages={ 'min_length': '用户名最少3位!', 'max_length': '用户账户最大8位', 'required': '用户名不能为空!', }, # 添加一个form-control类 widget=widgets.TextInput(attrs={'class':'form-control'})) password = forms.CharField(max_length=10, min_length=4, label='密码', error_messages={ 'min_length': '密码最少4位!', 'max_length': '密码最大10位', 'required': '密码不能为空!', }, widget=widgets.PasswordInput(attrs={'class':'form-control'})) re_password = forms.CharField(max_length=10, min_length=4, label='确认密码', error_messages={ 'min_length': '密码最少4位!', 'max_length': '密码最大10位', 'required': '密码不能为空!', }, widget=widgets.PasswordInput(attrs={'class':'form-control'})) email = forms.EmailField(label='邮箱', error_messages={ 'invalid': '邮箱格式不正确', 'required': '邮箱不能为空', }, widget=widgets.TextInput(attrs={'class':'form-control'}))
- views.py 文件
from app01.myform import RegisterForm def register(request): if request.method == 'POST': form_data = RegisterForm(request.POST) # request.POST可以看成是一个字典,直接传给forms类校验 if form_data.is_valid(): print(form_data.cleaned_data) return HttpResponse('校验成功') else: # 将带有错误信息的form对象返回前端页面
print(form_data.errors)
return render(request, 're_form.html', {'form': form_data})
form_data = RegisterForm() # 将form对象传给前端 return render(request, 're_form.html', {'form': form_data})
- re_form.html 文件
<form action="" method="post" novalidate> {% for foo in form %} {# 在后方设置一个span标签用来放错误提示 #} <p>{{ foo.label }}:{{ foo }} <span style="color: red">{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit" class="btn btn-block btn-success" value="注册"> </form>
ps : <form action="" method="post" novalidate>
: form 表单标签内的 novalidate
表示前端不进行校验。要使得错误提示信息显示为中文的话,除了上面的自定义错误提示信息以外,还可以修改配制文件里的语言环境:LANGUAGE_CODE= 'zh-hans'
widget参数
可以控制生成标签的各项属性
from django import widgets
使用
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})) # 设置为密文显示,并且增加样式,可添加多个,空格隔开即可
forms组件三种校验方式
forms组件针对字段数据的校验,提供了三种类型的校验方式(可以一起使用)
- 第一种类型:直接填写参数 max_length
- 第二种类型:使用正则表达式 validators
- 第三种类型:钩子函数 编写代码自定义校验规则
正则校验
要先导入一个正则校验模块
from django.core.validators import RegexValidator class MyForm(Form): phone = forms.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
七.forms组件之全局钩子, 局部钩子(重要)
1.钩子函数
- 全局和局部钩子都是定义在forms组件之内的函数
- 钩子函数类似于校验的第二道关卡, 能够让我们自定义校验规则
- 在字段所有的校验参数之后触发
2.两种钩子函数
- 局部钩子 : 用于给单个字段增加校验规则
- 全局钩子 : 用于给多个字段增加校验规则
3.两种钩子函数的书写方式
-
局部钩子
def clean_字段名(self): 字段值 = self.cleaned_data.get('字段名') [判断逻辑代码] [不通过抛出 ValidationError 异常] [通过则返回该字段值]
-
全局钩子
def clean(self): 字段值1 = self.cleaned_data.get('字段1') 字段值2 = self.cleaned_data.get('字段2') [判断逻辑代码] [不通过抛出 ValidationError 异常] [通过则 return self.cleaned_data]
注意:钩子函数要写在类里面
4.钩子示例
- 需求一 : 校验用户名不能有敏感字眼
- 需求二 : 校验两次输入的密码需一致
# myform.py 新增钩子函数
# 局部钩子 def clean_name(self): name = self.cleaned_data.get('name') for i in ['爱德华','sb']: if i in name: raise ValidationError(f'用户名中包含敏感词汇:"{i}"') else: return name # 全局钩子 def clean(self): password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') if password == re_password: return self.cleaned_data else: raise ValidationError('两次密码不一致')
# views.py 文件
from app01.myform import RegisterForm def register(request): if request.method == 'POST': form_data = RegisterForm(request.POST) # 将前端post请求的数据传入 if form_data.is_valid(): return HttpResponse('校验成功') else: # 获取到 ValidationError 中的 error 信息传给前端 error = form_data.errors.get('__all__') return render(request, 're_form.html', {'form': form_data,'error':error}) form_data = RegisterForm() # 将form空对象传给前端 return render(request, 're_form.html', {'form': form_data})
# re_form.html 文件
<form action="" method="post" novalidate> {% for foo in form %} <p>{{ foo.label }}:{{ foo }} <span style="color: red; display:inline-block">{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit" class="btn btn-block btn-success" value="注册"> {# 判断错误信息是否为None,是None就不渲染error #} {% if error is None %} <span style="color: red; display:inline-block"></span> {% else %} <span style="color: red; display:inline-block">{{ error }}</span> {% endif %} </form>
八.error错误提示为何是ul标签分析
- 上面我们在 views.py 文件中写到的,校验时打印错误提示信息
else: print('检验失败') # 获得检验失败的错误提示 errors print(form_data.errors)
- 查看打印结果 :
为什么是一个ul
标签格式, 一定是重写了 __str__
方法
- 我们导入 ErrorDict 来查看内部源码
from django.forms.utils import ErrorDict
发现其内部重写了__str__
方法, 返回的是 as_ul
方法, 该方法内部就是定义了html的格式并返回
并且还有一些其他的方法 : 比如 json 格式, data 数据, text 格式等等
而返回 as_ul
方法则是为了方便渲染模板
九.常用字段及插件
1. 初始值 : initial
username = forms.CharField( min_length=8, label="用户名", initial="shawn" # 设置默认值 )
2. 自定义错误提示 : error_massages
username = forms.CharField( min_length=8, label="用户名", initial="shawn", error_messages={ "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" } )
3. 密码框 : password
# 设置render_value在输入错误的时候, 保留输入的内容value值 pwd = forms.CharField( min_length=4, label="密码", widget=forms.widgets.PasswordInput(attrs={'class': 'a1'}, render_value=True) )
4. 反选radio
gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "未知")), label="性别", initial=3, widget=forms.widgets.RadioSelect() )
5. 单选checkbox
keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() )
6. 多选checkbox
Copyhobbies1 = forms.MultipleChoiceField( choices=((1, "read"), (2, "run"), (3, "play"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() )
7. 单选Select
Copyeducation = forms.ChoiceField( choices=((1, "高中"), (2, "中专"), (3, "大专"), (4, "本科"), (5, "研究生"), (6, "硕士")), label="学历", initial=3, widget=forms.widgets.Select(attrs={'class': 'form-control'}) )
8. 多选Select
Copyhobbies2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 2], widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'}) )
9.更多插件(了解)
插件名称 | 对应input |
TextInput | input type=“text” |
PasswordInput | input type=“password” |
HiddenInput | input type=“hidden” |
NumberInput | input type=“number” |
EmailInput | input type=“email” |
URLInput | input type=“url” |
Textarea | textarea |
DateInput | input type=“data” |
DateTimeInput | input type=“datetime” |
TimeInput | 只获取时分秒 |
CheckboxInput | input type=“checkbox” |
CheckboxSelectMultiple | 多选按钮 |
Select | select框 |
NullBooleanSelect | select框 三个选项 0 1 null |
SelectMultiple | 可多选的select框 |
RadioSelect | input type=“radion” |
FileInput | 可以查看当前目录下的所有文件。作用不大 |
十、modelform组件
什么是modelform组件?
这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来。先来一个简单的例子来看一下这个东西怎么用:
比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?
首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后天一个一个接收用户的输入,创建一个新的学生对象,保存起来。
--用之前学的方式创建模型表,很慢很繁琐。
--我们现在有个更方便的方法:ModelForm
使用校验性组件的目的
- 我们学习校验性组件的目的,绝大部分是为了数据录入数据库之前的各项审核
- forms组件使用的时候需要对照模型类编写代码,不够方便
- forms组件的强化版本,更好用更简单更方便!!!
常用参数介绍
1 model = models.Book # 对应的Model中的类 2 fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段 3 exclude = None # 排除的字段 4 labels = None # 提示信息 5 help_texts = None # 帮助提示信息 6 widgets = None # 自定义插件 7 error_messages = None # 自定义错误信息
代码展示
modelform提供了一个save方法,可以帮我们保存数据。(可以代替ORM的create和update操作)
from django import forms from app01 import models class MyModelForm(forms.ModelForm): class Meta: model = models.UserInfo fields = '__all__' labels = { 'username':'用户名' } def ab_mf_func(request): modelform_obj = MyModelForm() if request.method == 'POST': modelform_obj = MyModelForm(request.POST,instance=User_obj) # 加上instance参数之后,.save()就是更新的意思 if modelform_obj.is_valid(): modelform_obj.save() # models.UserInfo.objects.create(...)/update(...) else: print(modelform_obj.errors) return render(request, 'modelFormPage.html', locals())