从前端提交的各种数据可能存缺少必要字段以及包含非法数据等问题, 并且通常需要进行类型转换后才可以交由业务逻辑处理.

我们当然可以在控制器(Django的views函数)中完成这些工作, 但是这样会使控制器变得非常臃肿降低可维护性.

django.forms.Form是表单校验器的基类, 内置了必备的转换和校验逻辑并且可以让我们方便的自定义校验逻辑.

再说明如何自定义校验逻辑之前, 先介绍下Form的校验逻辑:

调用各字段的to_python()方法: 将请求参数转换成对应的python数据类型.若转换失败则抛出ValidationError.

# django.forms.CharField.to_python
def to_python(self, value):
"Returns a Unicode object."
if value in self.empty_values:
return ''
value = force_text(value)
if self.strip:
value = value.strip()
return value

调用各字段的validate()方法: 进行长度, 正则匹配等内置校验规则, 若不满足校验条件则抛出ValidationError.必要时可以重载该方法.

# django.forms.Form.validate
def validate(self, value):
if value in self.empty_values and self.required:
raise ValidationError(self.error_messages['required'], code='required')

调用各字段的run_validators()方法运行字段的所有自定义Validator, 并将所有的错误信息聚合成一个单一的ValidationError, 通常不需要重载该方法.

# django.forms.Form.run_validators
def run_validators(self, value):
if value in self.empty_values:
return
errors = []
for v in self.validators:
try:
v(value)
except ValidationError as e:
if hasattr(e, 'code') and e.code in self.error_messages:
e.message = self.error_messages[e.code]
errors.extend(e.error_list)
if errors:
raise ValidationError(errors)

各字段的clean()方法负责控制上述流程的执行, 并将清洗过的数据放入表单中名为cleaned_data的字典中:

def clean(self,  value):
"""
Validates the given value and returns its "cleaned" value as an
appropriate Python object. Raises ValidationError for any errors.
"""
value = self.to_python(value)
self.validate(value)
self.run_validators(value)
return value

最后调用表单子类中各字段的自定义校验器.自定义校验器可以从Form.cleaned_data中取到经过上述步骤处理过的数据.

下面将展示如何编写一个注册表单校验器:

from django import forms

class RegisterForm(forms.Form):
username = forms.RegexField(
required=True,
max_length=30,
regex='^[\w|\d]+$',
error_messages={
'required': ‘ERROR_USERNAME_REQUIRED’,
'max_length': 'ERRROR_FIELD_TOO_LONG',
'invalid': 'ERROT_INVALID_USERNAME',
},
label='username') password1 = forms.CharField(
required=True,
max_length=30,
error_messages={
'required': 'ERROR_PASSWD_REQUIRED',
'max_length': 'ERRROR_FIELD_TOO_LONG',
},
label='password1', widget=forms.PasswordInput) password2 = forms.CharField(
required=True,
max_length=30,
error_messages={
'required': 'ERROR_PASSWD_REQUIRED',
'max_length': 'ERRROR_FIELD_TOO_LONG',
},
label='password2', widget=forms.PasswordInput) email = forms.EmailField(required=False) gender = forms.ChoiceField(required=True, choices=[0, 1] def clean_username(self):
username = self.cleaned_data['username']
exist = User.objects.filter(username=username).count()
if exist:
raise forms.ValidationError('ERROR_ACCOUNT_EXISTED')
else:
self.cleaned_data['username'] = username
return self.cleaned_data['username'] def clean_password1(self):
if self.cleaned_data['password2'] != self.cleaned_data['password1']:
raise forms.ValidationError('ERROR_PASSWD_NOT_SAME')
self.cleaned_data['password1'] = encrypt(self.cleaned_data['password1'])
return self.cleaned_data['password1']

required是每个字段都有的校验规则, 其为True时字段值不允许为空.

error_messages是一个字典, 当某条规则未满足时就会抛出其对应的错误信息.

我们也可以在字段的构造器中使用validators=[]关键字参数添加校验器, 它们由run_validators方法控制调用并汇总错误信息.

label用可读性较高的方式描述字段, widget则指定输入组件.

上述代码中展示了部分字段, 我们常用的字段类型有:

下面的示例展示了如何在views函数中使用表单:

def register(request):
if request.method != 'POST':
raise Http404
form = RegisterForm(data=request.POST)
if not form.is_valid():
raise MyInvalidFormError(form.errors)
username = form.cleaned_data['username']
passsword = form.cleaned_data['password1']
if form.cleaned_data.get('email'):
email = form.cleaned_data['email']
else:
email = default_email
user = User.objects.create_data(username, email, password)
user.save()
return render(request, 'success.html')

在构造表单时使用data关键字参数将原始数据交给表单进行处理, 调用表单的is_valid()方法或访问errors属性时将触发Form的校验流程.

我们可以在errors属性获得校验中产生的所有错误信息, 校验完成后可以在cleaned_data属性中访问处理完的数据.

最新文章

  1. EF(Entity Framework)系统学习系列
  2. php上传文件类型
  3. vs2010连接postgresql数据库
  4. 读书摘要,Hackable Projects
  5. 读书笔记——Windows环境下32位汇编语言程序设计(6)使用浮点指令进行64位除法
  6. .NET 4.0 MemoryCache with SqlChangeMonitor
  7. java应用程序和虚拟机实例之间的关系
  8. Servlet细节
  9. java连接oracle数据库的实现代码
  10. epub-2格式电子书剖析之一:文档构成
  11. rsyslog 日志归类思路--根据syslog local5 nginx-zjzc01;
  12. sublimeText3使用记录
  13. 一个SQL存储过程面试题(比较简单)
  14. .net core 2.1 Ef 连接Mysql数据库 DB first
  15. 【转】Python3 日期时间 相关模块(time(时间) / datatime(日期时间) / calendar(日历))
  16. 对Swoole、Workerman和php自带的socket的理解
  17. Python--Virtualenv简明教程
  18. hive中 regexp_replace的用法,替换特殊字符问题
  19. Centos5 下redmine的安装及配置
  20. shell出现syntax error near unexpected token `<' 解决方法

热门文章

  1. 根据日期 读取三个csv不留指定日期的内容 新保存一个文件
  2. jquery移动端日期插件
  3. LINUX内核分析第七周学习总结:可执行程序的装载
  4. Repeater展示表格
  5. redis配置认证密码
  6. 解决C# 转到定义时打开的是元数据文件而非源代码文件的问题
  7. java线程的理解
  8. Selenium2+python自动化27-查看selenium API
  9. Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger
  10. android开发学习---layout布局、显示单位和如何进行单元测试