使用Form组件验证
views:
from django.shortcuts import render, HttpResponse
from django import forms
# 先定义一个Form类
class LoginForm(forms.Form):
name = forms.CharField(
label='用户名',
initial='陌生人',
strip=True,
error_messages={
'required': '用户名不能为空',
}
)
# 密码框
password = forms.CharField(
label='密码',
min_length=6,
widget=forms.PasswordInput(render_value=True),
error_messages={
'required': '密码不能为空',
'min_length': '密码不能小于6位',
}
)
# 单选框
gender = forms.ChoiceField(
choices=[(1, '男'), (2, '女'), (3, '保密')],
label='性别',
initial=3,
widget=forms.RadioSelect(),
error_messages={
'required': '请选择性别'
}
)
# 下拉单选
hobby = forms.ChoiceField(
label='爱好',
widget=forms.Select(),
choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')),
initial=2,
)
# 多选
hobby2 = forms.MultipleChoiceField(
label='爱好2',
choices=((1, '摩托车'), (2, '汽车'), (3, '游艇')),
initial=[1, 3],
widget=forms.SelectMultiple()
)
# 单选checkbox
keep = forms.ChoiceField(
label='是否记住密码',
initial='checked',
widget=forms.CheckboxInput()
)
# 多选checkbox
city = forms.ChoiceField(
label='居住城市',
choices=[(1, '北京'), (2, '天津'), (3, '上海'), (4, '武汉')],
initial=4,
widget=forms.Select()
)
# 写一个函数视图
def login(request):
form_obj = LoginForm()
if request.method == 'POST':
form_obj = LoginForm(request.POST)
if form_obj.is_valid():
pass
return render(request, 'app/login.html', {'form_obj': form_obj})
templates:
<form action="/app/login/" method="POST" novalidate>
{% csrf_token %}
<p>
<label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
{{ form_obj.name }}
<span>{{ form_obj.name.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.password.id_for_label }}">{{ form_obj.password.label }}</label>
{{ form_obj.password }}
<span>{{ form_obj.password.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.gender.id_for_label }}">{{ form_obj.gender.label }}</label>
{{ form_obj.gender }}
<span>{{ form_obj.gender.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.hobby.id_for_label }}">{{ form_obj.hobby.label }}</label>
{{ form_obj.hobby }}
<span>{{ form_obj.hobby.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.hobby2.id_for_label }}">{{ form_obj.hobby2.label }}</label>
{{ form_obj.hobby2 }}
<span>{{ form_obj.hobby2.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.keep.id_for_label }}">{{ form_obj.keep.label }}</label>
{{ form_obj.keep }}
<span>{{ form_obj.keep.errors.0 }}</span>
</p>
<p>
<label for="{{ form_obj.city.id_for_label }}">{{ form_obj.city.label }}</label>
{{ form_obj.city }}
<span>{{ form_obj.city.errors.0 }}</span>
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
常用Form组件内置字段
Field
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
validators=[], 自定义验证规则
localize=False, 是否支持本地化
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀
CharField(Field)
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白
IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值
DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度
DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'}
ChoiceField(Field)
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示
字段校验
RegexValidator
验证器
from django.core.validators import RegexValidator
no = forms.CharField(
label='员工编号',
validators=[RegexValidator(r'^[0-9]+', '请输入数字'), RegexValidator('^110[0-9]+$', '请以110开头')]
)
自定义函数验证
import re
from django.core.exceptions import ValidationError
def mobile_validate(value):
mobile_re = re.compile(r'^1[2356789]{1}[0-9]{9}$')
if not mobile_re.match(value):
raise ValidationError('手机号格式错误')
class LoginForm(forms.Form):
mobile = forms.CharField(
label='手机号',
validators=[mobile_validate, ],
error_messages={
'required': '手机号不能为空',
}
)
Hook方法
局部钩子
在Fom类中定义 clean_字段名()
方法,就能够实现对特定字段进行校验
class LoginForm(forms.Form):
description = forms.CharField(
label='内容描述',
initial='暂无描述',
min_length=4,
error_messages={
'required': '不能为空',
'invalid': '格式错误',
'min_length': '最少评论4个字'
}
)
def clean_description(self):
value = self.cleaned_data.get('description')
if '666' in value:
raise ValidationError('请不要喊666')
else:
return value
全局钩子
在Fom类中定义clean()
方法,就能够实现对字段进行全局校验
class LoginForm(forms.Form):
# 密码框
password = forms.CharField(
label='密码',
min_length=6,
widget=forms.PasswordInput(),
error_messages={
'required': '密码不能为空',
'min_length': '密码不能小于6位',
}
)
repassword = forms.CharField(
label='请再次输入密码',
min_length=6,
widget=forms.PasswordInput(),
error_messages={
'required': '密码不能为空',
'min_length': '密码不能小于6位',
}
)
def clean(self):
password_value = self.cleaned_data.get('password')
repassword_value = self.cleaned_data.get('repassword')
if password_value == repassword_value:
return self.cleaned_data
else:
self.add_error('repassword', '两次密码不一致')
源码分析
-
在执行
form_obj.is_valid()
进行判断 -
进入
is_valid
方法def is_valid(self): """ Returns True if the form has no errors. Otherwise, False. If errors are being ignored, returns False. """ return self.is_bound and not self.errors
self.is_bound
判断是否有数据self.errors
判断是否有错误信息 -
进入
errors
属性方法@property def errors(self): "Returns an ErrorDict for the data provided for the form" if self._errors is None: self.full_clean() return self._errors
-
进入
full_clean
方法self._errors
存放错误信息cleaned_data
存放验证通过的数据def full_clean(self): """ Cleans all of self.data and populates self._errors and self.cleaned_data. """ self._errors = ErrorDict() if not self.is_bound: # Stop further processing. return self.cleaned_data = {} # If the form is permitted to be empty, and none of the form data has # changed from the initial data, short circuit any validation. if self.empty_permitted and not self.has_changed(): return self._clean_fields() self._clean_form() self._post_clean()
-
执行
_clean_fields
方法,循环每个字段,分别校验def _clean_fields(self): for name, field in self.fields.items(): # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. if field.disabled: value = self.get_initial_for_field(field, name) else: value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) 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)
-
执行
_clean_form
方法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