form组件的校验功能
views.py
from django.shortcuts import render, HttpResponse # Create your views here. from django import forms from django.forms import widgets class UserForm(forms.Form): name = forms.CharField(min_length=4,label='用户名') # 必须用required pwd = forms.CharField(min_length=4,label='密码') r_pwd = forms.CharField(min_length=4,label='确认密码') email = forms.EmailField(label='邮箱') tel = forms.CharField(label='手机') def reg(request): if request.method == 'POST': # form = UserForm({'name': 'ed', 'email': '123@qq.com'}) # 加多少个键值都无所谓,只要能匹配全Form里的字段就会返回True # print(form.is_valid()) # 返回布尔值 form = UserForm(request.POST) # form表单的name属性值应该与forms组件字段名称一致 if form.is_valid(): print(form.changed_data) # 存的是通过校验的字典 else: print(form.cleaned_data) # {正确的键值,"email":"123@11.com"} print(form.errors) # {"name":["insure this value has at least 4 characters]} print(type(form.errors)) # ErrorDict print(form.errors.get('name')) print(type(form.errors.get('name'))) # ErrorList print(form.errors.get('name')[0]) # This field is required. ''' if 所有字段校验成功,则form.cleaned_data:{'name': 'edward', 'email': '123@qq.com'} ''' return render(request, 'register.html', locals()) form = UserForm() # get请求的form,渲染form组件用 return render(request, 'register.html', locals())
html
<form action="" method="post"> {% csrf_token %} <p>用户名:<input type="text" name="user"></p> <p>密码:<input type="password" name="pwd"></p> <p>确认密码:<input type="password" name="r_pwd"></p> <p>邮箱:<input type="email" name="email"></p> <p>手机号:<input type="text" name="tel"></p> <input type="submit" value="提交"> </form>
form组件渲染的三种方式
<h3>form组件渲染方式1</h3> <form action="" method="post"> {% csrf_token %} <p>{{ form.name.label }}:{{ form.name }} <span>{{ form.name.errors.0 }}</span></p> <p>{{ form.pwd.label }}:{{ form.pwd }} <span>{{ form.pwd.errors.0 }}</span></p> <p>{{ form.r_pwd.label }}:{{ form.r_pwd }} <span>{{ form.r_pwd.errors.0 }}</span></p> <p>{{ form.email.label }}:{{ form.email }} <span>{{ form.email.errors.0 }}</span></p> <p>{{ form.tel.label }}:{{ form.tel }} <span>{{ form.tel.errors.0 }}</span></p> <input type="submit" value="提交"> </form> <hr> <h3>form组件渲染方式2</h3> <form action="" method="post"> {% csrf_token %} {% for filed in form %} <p> <label for="">{{ filed.label }}</label> {{ filed }} </p> {% endfor %} <input type="submit" value="提交"> </form> <hr> <h3>form组件渲染方式3 --- 不建议使用这种方式,因为它只能固定放那么几个标签,缺乏灵活性,以后改动麻烦</h3> <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="提交"> </form>
class UserForm(forms.Form): name = forms.CharField(min_length=4, label='用户名', error_messages={"required": "该字段不能为空"}, widget=widgets.TextInput(attrs={'class': 'form-control'}) ) # 必须用要提示错误信息的关键词 pwd = forms.CharField(min_length=4, label='密码', widget=widgets.PasswordInput(attrs={'class': 'form-control'}), error_messages={ "required": "该字段不能为空" }) r_pwd = forms.CharField(min_length=4, label='确认密码', error_messages={"required": "该字段不能为空"}, widget=widgets.PasswordInput(attrs={'class': 'form-control'}), ) email = forms.EmailField(label='邮箱', error_messages={"required": "该字段不能为空", 'invalid': "格式错误"}, widget=widgets.TextInput(attrs={'class': 'form-control'}), ) tel = forms.CharField(label='手机', error_messages={"required": "该字段不能为空", }, widget=widgets.TextInput(attrs={'class': 'form-control'}), )
def clean_name(self): val = self.cleaned_data.get('name') ret = UserInfo.objects.filter(name=val) if not ret: return val else: raise ValidationError('该用户已注册!') def clean_tel(self): val = self.cleaned_data.get('tel') ret = UserInfo.objects.filter(tel=val) if len(ret) == 11: return val else: raise ValidationError('手机号格式错误') # 源码 def _clean_fields(self): try: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: value = field.clean(value) self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e : self.add_error(name, e)
全局钩子
为了解耦,新建一个myforms.py的文件,把有关forms的代码都放在这里面。
# 全局钩子 # 走完所有的校验才走clean def clean(self): # 走完所有的校验才走clean pwd = self.cleaned_data.get('pwd') r_pwd = self.cleaned_data.get('r_pwd') # 都满足不低于4个字符这个条件后才验证密码是否一致 if pwd and r_pwd: if pwd == r_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致') else: return self.cleaned_data #源码 def clean(self): """ Hook for doing any extra form-wide cleaning after Field.clean() has been called on every field. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. """ return self.cleaned_data def _clean_form(self): try: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data # 全局钩子在前端没有显示,要在处理post请求的代码里给错误信息一个变量并传到前端 errors = form.errors.get('__all__') # 当两次密码不一致时,在前端显示这个错误 <p> {{ form.r_pwd.label }}:{{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span> <span class="pull-right error">{{ errors.0 }}</span> </p>