【form组件】
需求:
写一个注册功能
获取用户名和密码 利用form表单提交数据
在后端判断用户和密码是否符合一定的条件
用户名中不能含有大傻子
密码不能少于三位数
如果符合条件,然后将提示信息展示到页面前端
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script> 7 <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css"> 8 </head> 9 <body> 10 <form action="" method="post"> 11 <p>username: 12 <input type="text" name="username"> 13 <span style="color: red">{{ back_dic.username }}</span> 14 </p> 15 <p>password:<input type="text" name="password"> 16 <span style="color: blue">{{ back_dic.password }}</span> 17 </p> 18 <input type="submit" value="提交" class="btn btn-info"> 19 20 </form> 21 </body> 22 </html>
然而form组件替我们省去了这些步骤,
form能够完成的事情:
1.渲染html代码
2.检验数据
3.展示提示信息
。
。
。
(数据的校验)
1 为什么数据校验非要在后端而不利用前端的js直接完成呢? 2 数据校验前端可以可无,但是后端必须要有 3 且前端的校验是弱不禁风的,可以直接修改,或是利用爬虫程序绕过前端页面直接朝后端提交数据, 4 所以在后端校验数据比较好 5 6 ------------------- 7 测试环境的准备: 8 1.可以自己拷贝代码准备 9 2.在pycharm里面其实也有一个准备好的测试环境---python Console
(校验的内部本质)
1 问题:多写了字段为什么返回的是True? 2 3 原因:校验数据只校验类里面有的字段,多传的字段不校验 4 5 问题2:少传字段返回什么? 6 7 不可以,返回false,原因必须传值
(views,py)
(必须完全符合需求才能返回True)
。
。
[渲染html标签,不会渲染提交按钮]
。
。
。
【展示错误信息提示】
一些字段意思
1 label 字段名 2 error_messages 错误提示信息 3 initial 初始值,默认值 4 required 是否必填 /require=false 5 widget=forms.widgets.TextInput(attrs={'class': 'form-control c1 c2'}) 控制类型样式 6 第一道关卡还支持正则校验 7 validators=[RegexValidator(r'^1\d{10}$', '手机格式错误')]
phone = forms.CharField(
validators=
[RegexValidator(r'^1[3-9]\d{9}$', '手机格式错误'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
],
)
views.py
# form组件校验信息 from django import forms class MyForm(forms.Form): # 字符串类型,错误信息提示为中文:error_messages+{错误类型解释} username = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={ 'required': '用户名不能为空', 'min_length': '用户名长度不能小于3', 'max_length': '用户名长度不能大于8', }) password = forms.CharField(min_length=3, max_length=8, label='密码') # email必须符合邮箱格式 email = forms.EmailField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'invalid': '邮箱格式错误', }) # 渲染html页面 def index(request): # 1.先产生一个空对象 form_obj = MyForm() # 展示错误信息提示 if request.method == 'POST': # 2.获取用户数据并校验,技能解决获取数据繁琐,也能解决检验数据需要的字典格式 # 1和2步骤的名称一定要一样 form_obj = MyForm(request.POST) # 判断数据是否合法 if form_obj.is_valid(): # 如果合法操作数据库 return HttpResponse('ok') # 不合法,展示错误信息到前端页面 # 3.直接将该空对象直接传个html页面 return render(request, 'index.html', locals())
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css"> {% block css %} {% endblock %} </head> <body> {#novalidate:浏览器不校验#} <form action="" method="post" novalidate> <p>第三种渲染方式:两者的结合</p> {% for form in form_obj %} <p>{{ form.label }}:{{ form }} {# 提示错误信息框:这时浏览器自动帮我们校验数据,不好#} {# form.errors.0:只拿到第一个错误信息,变成p便签#} <span style="color: red">{{ form.errors.0 }}</span> </p> {% endfor %} <input type="submit" class="btn btn-info"> </form> </body> </html>
总结:
。
。
。
【form组件钩子函数:HOOK*****】
1 钩子函数: 2 在特定的节点自动触发完成的响应操作 3 钩子函数在form组件中就类似于第二道关卡,能够让我们自定义校验规则 4 5 在form组件中有两类钩子 6 1.局部钩子 7 当需要给单个字段增加校验规则的时候可以使用 8 2.全局钩子 9 当需要给多个个字段增加校验规则的时候可以使用
views.py
class MyForm(forms.Form): # 字符串类型 username = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={ 'required': '用户名不能为空', 'min_length': '用户名长度不能小于3', 'max_length': '用户名长度不能大于8', }) password = forms.CharField(min_length=3, max_length=8, label='密码') re_password = forms.CharField(min_length=3, max_length=8, label='确认密码', error_messages={ 'required': '确认密码不能为空', 'min_length': '确认密码长度不能小于3', 'max_length': '确认密码长度不能大于8', }) # email必须符合邮箱格式 email = forms.EmailField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'invalid': '邮箱格式错误', }) ****************************************************************************钩子函数校验 # 钩子函数,只有第一道验证全部通过,才会执行钩子函数 # 局部钩子 def clean_username(self): username = self.cleaned_data.get('username') if '666' in username: # 添加错误信息展示 self.add_error('username', '用户名不能包含666') # 将钩子函数勾出来的数据再放回去 return username # 全局钩子 def clean(self): # 获取两个字段 password=self.cleaned_data.get('password') re_password=self.cleaned_data.get('re_password') if not password == re_password: self.add_error('re_password', '两次密码不一致') # 把所有钩子数据都返回 return self.cleaned_data
。
。
。
【form组件源码】
1 切入点 2 3 form_obj.is_valid() 4 5 源码: 6 def is_valid(self): 7 """Return True if the form has no errors, or False otherwise.""" 8 return self.is_bound and not self.errors 9 10 如果is_valid要返回True,那么self.is_bound要为True,且self.errors要为Flase 11 12 13 ---------------- 14 is_bound 15 16 def __init__( 17 self, 18 data=None, 19 files=None, 20 auto_id="id_%s", 21 prefix=None, 22 initial=None, 23 error_class=ErrorList, 24 label_suffix=None, 25 empty_permitted=False, 26 field_order=None, 27 use_required_attribute=None, 28 renderer=None, 29 ): 30 self.is_bound = data is not None or files is not None 31 其中data就是我们传进去的形参,只要传值了就为True 32 33 34 --------------------------------- 35 errors:默认为None 36 37 @property 38 def errors(self): 39 """Return an ErrorDict for the data provided for the form.""" 40 if self._errors is None: 41 self.full_clean() 42 return self._errors 43 44 form组件所有的功能基本都出自于该方法 45 46 def full_clean(self): 47 """ 48 Clean all of self.data and populate self._errors and self.cleaned_data. 49 """ 50 self._errors = ErrorDict() 51 if not self.is_bound: # Stop further processing. 52 return 53 self.cleaned_data = {} 54 # If the form is permitted to be empty, and none of the form data has 55 # changed from the initial data, short circuit any validation. 56 if self.empty_permitted and not self.has_changed(): 57 return 58 **************************** 59 self._clean_fields() # 校验字段 60 self._clean_form() # 全局钩子 61 self._post_clean()