Django之Form组件
一、Form组件介绍
Form组件可以做的几件事情:
1、用户请求数据验证
2、自动生成错误信息
3、打包用户提交的正确信息
4、如果其中有一个错误了,其他的正确,保留上次输入的内容
5、自动创建input标签并可以设置样式
Form组件的基本用法
1 类 2 字段 3 is_valid() 4 cleaned_data 5 errors 6 字段参数: 7 max_length 8 min_length 9 validators = [RegexValidators("xxx")] 10 11 钩子函数 12 clean_字段名 13 注意: 14 必须有返回值 15 只能拿自己当前字段值 16 raise ValidationError("xxx") 17 下拉框数据源时时更新 18 1、重写init方法 19 先执行父类构造方法 20 self.fields["xx"].choices = xxxxx 21 2、ModelChoiceField
二、Form组件的使用
1、创建规则
1 class Foo(Form): #必须继承 2 username = xxx 3 password = xxx 4 email = xxx 5 注意这里的字段必须和input的name字段一致
2、数据和规则进行匹配
先导入需要用到的模块
from django.forms import Form from django.forms import fields from django.forms import widgets
views.py
1 from django.shortcuts import render,redirect 2 from app01 import models 3 # Create your views here. 4 from django.forms import Form 5 from django.forms import fields 6 from django.forms import widgets 7 # 1、创建规则 8 class TeacherForm(Form): #必须继承Form 9 # 创建字段,本质上是正则表达式 10 username = fields.CharField( 11 required=True, #必填字段 12 error_messages={"required":"用户名不能为空!!"}, #显示中文错误提示 13 widget=widgets.TextInput(attrs={"placeholder":"用户名","class":"form-control"}) #自动生成input框 14 ) 15 password = fields.CharField(required=True, error_messages={'required': '密码不能为空'}, 16 widget=widgets.TextInput(attrs={'placeholder': '密码', 'class': 'form-control'})) # 不能为空 17 email = fields.EmailField( 18 required=True, 19 error_messages={"required":"邮箱不能为空!!","invalid":"无效的邮箱"}, 20 widget=widgets.EmailInput(attrs={"placeholder": "邮箱", "class": "form-control"}) # 自动生成input框 21 ) #不能为空且邮箱格式要一致 22 23 # 2、使用规则:将数据和规则进行匹配 24 def teacherindex(request): 25 teacher_obj = models.UserInfo.objects.all() 26 # print(teacher_obj) 27 return render(request,"teacherindex.html",{"teacher_obj":teacher_obj}) 28 def add(request): 29 if request.method=="GET": 30 form = TeacherForm() #只是让显示一个input框 31 return render(request,"add.html",{"form":form }) 32 else: 33 form = TeacherForm(data=request.POST) 34 # print(form) #<QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]> 35 if form.is_valid():# 开始验证 36 # print('执行成功',form.cleaned_data) # 所有匹配成功,字典 37 # {'username': 'asd', 'password': 'sdf', 'email': 'sadf@live.com','ut_id':1} 38 form.cleaned_data['ut_id'] = 1 #要分的清是班主任还是讲师 39 models.UserInfo.objects.all().create(**form.cleaned_data) 40 return redirect("/teacherindex/") 41 else: 42 # print("=====?",form.errors,type(form.errors))#返回失败的结果 43 # print(form.errors["username"][0]) #拿到返回失败的结果,渲染到页面 44 return render(request,"add.html",{"form":form})
html
{% block right %} <h1>添加老师信息</h1> <hr> <form method="post" novalidate> {% csrf_token %} <p>姓名:{{ form.username }}</p>{{ form.errors.username.0 }} <p>密码:{{ form.password }}</p>{{ form.errors.password.0 }} <p>邮箱:{{ form.email }}</p>{{ form.errors.email.0 }} <p><input type="submit" value="提交"></p> </form> {% endblock %}
需要注意的几个点:
1.如果访问视图的是一个GET
请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一个访问该URL 时会发生的情况。
2.如果表单的提交使用POST
请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:form = NameForm(request.POST)
。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。
3.我们调用表单的is_valid()
方法;如果它不为True
,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。
如果is_valid()
为True
,我们将能够在cleaned_data
属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。
4.
form = TeacherForm() #没有参数,只是一个input框
form = TeacherForm(data=request.POST) # 数据和规则放置一起 (添加的时候用)
form = TeacherForm(initial={'username':obj.username,'password':obj.password,'email':obj.email}) # 显示input,并且将数据库中的默认值填写到input框中 (编辑的时候用)
Widgets
每个表单字段都有一个对应的Widget
类,它对应一个HTML 表单Widget
,例如<input type="text">
。
在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField
具有一个TextInput Widget
,它在HTML 中生成一个<input type="text">
。
局部钩子和全局钩子
局部钩子
1 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError 2 def clean_name(self): 3 4 val=self.cleaned_data.get("name") 5 6 ret=UserInfo.objects.filter(name=val) 7 8 if not ret: 9 return val 10 else: 11 raise ValidationError("该用户已注册!") 12 13 def clean_tel(self): 14 15 val=self.cleaned_data.get("tel") 16 17 if len(val)==11: 18 19 return val 20 else: 21 raise ValidationError("手机号格式错误")
全局钩子
1 def clean(self): 2 pwd=self.cleaned_data.get('pwd') 3 r_pwd=self.cleaned_data.get('r_pwd') 4 5 if pwd and r_pwd: 6 if pwd==r_pwd: 7 return self.cleaned_data 8 else: 9 raise ValidationError('两次密码不一致') 10 else: 11 12 return self.cleaned_data
字段的数据
不管表单提交的是什么数据,一旦通过调用is_valid()
成功验证(is_valid()
返回True
),验证后的表单数据将位于form.cleaned_data
字典中。这些数据已经为你转换好为Python 的类型。
注:此时,你依然可以从request.POST
中直接访问到未验证的数据,但是访问验证后的数据更好一些。
在上面的联系表单示例中,is_married将是一个布尔值。类似地,IntegerField
和FloatField
字段分别将值转换为Python 的int
和float
。
第二种方式:
创建字段,定义规则
1 class TeacherForm(Form): #必须继承Form 2 # 创建字段,本质上是正则表达式 3 username = fields.CharField( 4 required=True, #必填字段 5 error_messages={"required":"用户名不能为空!!"}, #显示中文错误提示 6 widget=widgets.TextInput(attrs={"placeholder":"用户名","class":"form-control"}), #自动生成input框 7 label="姓名", 8 label_suffix=":" 9 ) 10 password = fields.CharField(required=True, error_messages={'required': '密码不能为空'}, 11 widget=widgets.PasswordInput(attrs={'placeholder': '密码', 'class': 'form-control'}), 12 label="密码", 13 label_suffix=":" 14 ) # 不能为空 15 16 email = fields.EmailField( 17 required=True, 18 error_messages={"required":"邮箱不能为空!!","invalid":"无效的邮箱"}, 19 widget=widgets.EmailInput(attrs={"placeholder": "邮箱", "class": "form-control"}), # 自动生成input框 20 label = "邮箱", 21 label_suffix = ":" 22 ) #不能为空且邮箱格式要一致
页面上渲染
附上forms组件测试代码
my_forms
1 from django import forms 2 3 from django.forms import widgets 4 from app01.models import UserInfo 5 6 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError 7 8 class UserForm(forms.Form): 9 name=forms.CharField(min_length=4,label="用户名",error_messages={"required":"该字段不能为空"}, 10 widget=widgets.TextInput(attrs={"class":"form-control"}) 11 ) 12 pwd=forms.CharField(min_length=4,label="密码", 13 widget=widgets.PasswordInput(attrs={"class":"form-control"}) 14 ) 15 r_pwd=forms.CharField(min_length=4,label="确认密码",error_messages={"required":"该字段不能为空"},widget=widgets.TextInput(attrs={"class":"form-control"})) 16 email=forms.EmailField(label="邮箱",error_messages={"required":"该字段不能为空","invalid":"格式错误"},widget=widgets.TextInput(attrs={"class":"form-control"})) 17 tel=forms.CharField(label="手机号",widget=widgets.TextInput(attrs={"class":"form-control"})) 18 19 20 def clean_name(self): 21 22 val=self.cleaned_data.get("name") 23 24 ret=UserInfo.objects.filter(name=val) 25 26 if not ret: 27 return val 28 else: 29 raise ValidationError("该用户已注册!") 30 31 def clean_tel(self): 32 33 val=self.cleaned_data.get("tel") 34 35 if len(val)==11: 36 37 return val 38 else: 39 raise ValidationError("手机号格式错误") 40 41 def clean(self): 42 pwd=self.cleaned_data.get('pwd') 43 r_pwd=self.cleaned_data.get('r_pwd') 44 45 if pwd and r_pwd: 46 if pwd==r_pwd: 47 return self.cleaned_data 48 else: 49 raise ValidationError('两次密码不一致') 50 else: 51 52 return self.cleaned_data 53 54 myforms
视图函数
1 from django.shortcuts import render,HttpResponse 2 3 # Create your views here. 4 5 6 7 from app01.myforms import * 8 9 10 def reg(request): 11 12 if request.method=="POST": 13 14 print(request.POST) 15 16 #form=UserForm({"name":"yu","email":"123@qq.com","xxxx":"alex"}) 17 18 19 form=UserForm(request.POST) # form表单的name属性值应该与forms组件字段名称一致 20 21 print(form.is_valid()) # 返回布尔值 22 23 if form.is_valid(): 24 print(form.cleaned_data) # {"name":"yuan","email":"123@qq.com"} 25 else: 26 print(form.cleaned_data) # {"email":"123@qq.com"} 27 # print(form.errors) # {"name":[".........."]} 28 # print(type(form.errors)) # ErrorDict 29 # print(form.errors.get("name")) 30 # print(type(form.errors.get("name"))) # ErrorList 31 # print(form.errors.get("name")[0]) 32 33 34 # 全局钩子错误 35 #print("error",form.errors.get("__all__")[0]) 36 errors=form.errors.get("__all__") 37 38 39 return render(request,"reg.html",locals()) 40 41 ''' 42 43 form.is_valid() :返回布尔值 44 form.cleaned_data :{"name":"yuan","email":"123@qq.com"} 45 form.errors :{"name":[".........."]} 46 47 ''' 48 49 50 form=UserForm() 51 52 return render(request,"reg.html",locals()) 53 54 视图函数
模板文件
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 .error{ 8 color: red; 9 } 10 </style> 11 <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> 12 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" 13 integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> 14 </head> 15 <body> 16 17 <div class="container"> 18 19 <div class="row"> 20 <div class="col-md-6 col-lg-offset-3"> 21 22 {#<h3>简单form</h3>#} 23 {##} 24 {##} 25 {#<form action="" method="post" novalidate>#} 26 {# {% csrf_token %}#} 27 {# <p>用户名<input type="text" name="name"></p>#} 28 {# <p>密码 <input type="text" name="pwd"></p>#} 29 {# <p>确认密码 <input type="text" name="r_pwd"></p>#} 30 {# <p>邮箱 <input type="text" name="email"></p>#} 31 {# <p>手机号 <input type="text" name="tel"></p>#} 32 {# <input type="submit">#} 33 {##} 34 {#</form>#} 35 36 <hr> 37 <h3>forms组件渲染方式1</h3> 38 <form action="" method="post" novalidate> 39 40 {% csrf_token %} 41 <p>{{ form.name.label }} 42 {{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span> 43 </p> 44 <p>{{ form.pwd.label }} 45 {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span> 46 </p> 47 <p>确认密码 48 {{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span class="pull-right error">{{ errors.0 }}</span> 49 </p> 50 <p>邮箱 {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></p> 51 <p>手机号 {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span></p> 52 <input type="submit"> 53 54 </form> 55 56 {#<h3>forms组件渲染方式2</h3>#} 57 {##} 58 {#<form action="" method="post" novalidate>#} 59 {# {% csrf_token %}#} 60 {##} 61 {# {% for field in form %}#} 62 {##} 63 {# <div>#} 64 {# <label for="">{{ field.label }}</label>#} 65 {# {{ field }}#} 66 {# </div>#} 67 {##} 68 {# {% endfor %}#} 69 {##} 70 {# <input type="submit">#} 71 {#</form>#} 72 {##} 73 {#<h3>forms组件渲染方式3</h3>#} 74 {##} 75 {#<form action="" method="post">#} 76 {# {% csrf_token %}#} 77 {##} 78 {# {{ form.as_p }}#} 79 {##} 80 {# <input type="submit">#} 81 {#</form>#} 82 83 84 </div> 85 </div> 86 </div> 87 88 </body> 89 </html> 90 91 模版文件