上一章讲了使用formalchemy进行服务端验证,但要开发一个用户体验良好的网站,客户端验证是必不可少的,用户能够在填写的时候就获取到错误信息,没必要等待提交的过程,大大节省了用户的时间。 javascript
Formalchemy进行客户端验证,其实就是使用前端的js插件定义验证方法,而后在formalchemy中为字段赋予特殊的属性,让验证插件从属性了解某个字段须要进行什么验证,例如,咱们在User表中定义email字段必须为email形式数据,那么formalchemy会在输出的表单中设置email字段的type为email,前台使用的插件能识别type="email"的input,而后进行email格式验证。 css
这里介绍一个我经常使用的jquery验证插件validation,这个插件自己已内置了不少经常使用的验证方法,若是有特殊的验证需求,还能够进行自定义,有兴趣的同窗能够谷歌下。下面咱们就以jquery的validation为验证插件,结合formalchemy来介绍下如何进行客户端验证。 html
首先咱们下载validation,放入static中,并在模板中引用,form.html模板修改以下
form.html 前端
<!DOCTYPE html> <html> <head> <title></title> <link type="text/css" href="/static/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="/static/js/jquery.min.js"></script> <script type="text/javascript" src="/static/js/bootstrap.min.js"></script> <script type="text/javascript" src="/static/js/jquery.metadata.js"></script><!--引入该插件能够在input中用metadata的形式设置验证规则--> <script type="text/javascript" src="/static/js/jquery.validate.min.js"></script> <script type="text/javascript" src="/static/js/additional-methods.min.js"></script><!--validation的内置验证规则--> <script type="text/javascript" src="/static/js/messages_zh.js"></script><!--validation的中文多语言包--> <script type="text/javascript" src="/static/js/base.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="span6 offset3"> <form class="form-horizontal validate" action="" method="post"> {{ form.render() }} <div style="text-align: center;"> <input type="submit" class="btn btn-large" value="Submit" /> </div> </form> </div> </div> </div> </body> </html>其中base.js是咱们将要进行验证设置的js文件,代码以下
$(function(){ //重定义validation的remote验证方法,让其传递的值字段名所有为val,并使用post方法 $.validator.methods.remote = function(value, element, param) { if ( this.optional(element) ) { return "dependency-mismatch"; } var previous = this.previousValue(element); if (!this.settings.messages[element.name] ) { this.settings.messages[element.name] = {}; } previous.originalMessage = this.settings.messages[element.name].remote; this.settings.messages[element.name].remote = previous.message; param = typeof param === "string" && {url:param} || param; if ( this.pending[element.name] ) { return "pending"; } if ( previous.old === value ) { return previous.valid; } previous.old = value; var validator = this; this.startRequest(element); var data = {}; data["val"] = value; $.ajax($.extend(true, { url: param, mode: "abort", port: "validate" + element.name, dataType: "json", data: data, type: "post", success: function(response) { validator.settings.messages[element.name].remote = previous.originalMessage; var valid = response === true || response === "true"; if ( valid ) { var submitted = validator.formSubmitted; validator.prepareElement(element); validator.formSubmitted = submitted; validator.successList.push(element); delete validator.invalid[element.name]; validator.showErrors(); } else { var errors = {}; var message = response || validator.defaultMessage( element, "remote" ); errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message; validator.invalid[element.name] = true; validator.showErrors(errors); } previous.valid = valid; validator.stopRequest(element, valid); } }, param)); return "pending"; }; var validate_form = $("form.validate");//定义class为validate的表单须要进行验证 validate_form.each(function(){ $(this).validate({ errorClass: "help-inline",//设置错误信息使用的class,能够自定义该class的样式 errorElement: "span",//设置错误信息包括在什么元素中 //设置用户输入错误时事件回调 errorPlacement: function(error, element){ element.after(error); } }); }); });
接下来咱们重温下User表单的验证需求: java
class unique python
class unique(BaseView): def __init__(self): super(unique, self).__init__() #设置白名单,让须要进行验证的表和字段才能够在该视图中查询 self.allow = [ ("User", "name"), ("User", "email"), ] def POST(self, model, field, action): if (model, field) not in self.allow: raise web.notfound() data = web.input() m = globals()[model] try: id = int(action) except: query = self.db.query(m).filter(getattr(m, field)==data.val).first() else: query = self.db.query(m).filter( (getattr(m, field)==data.val) & (m.id!=id) ).first() if not query: return "true" else: return "false"并在urls改成如下:
urls = ( "/unique/(.*)/(.*)/(.*)/", "unique", "/", "index", "/form/", "showform", )
最后咱们在forms.py中进行with_html的定义,就能在输出的表单中设置须要告诉验证插件的信息了,forms.py修改以下: jquery
forms.py
web
#-*- coding:utf-8 -*- from formalchemy import config, validators, Field, FieldSet from customEngine import Jinja2Engine from models import * from customValidators import * class UserForm: def __init__(self): #这里的directories是指表单模板存放的地方,咱们在第二章提到的templates下建立一个文件夹,命名为form config.engine = Jinja2Engine(directories=["templates/form"]) #为表单设置label def setLabel(self): self.name = self.fs.name.label("User Name") self.email = self.fs.email.label("Email Address") self.password = self.fs.password.label("Password") self.superuser = self.fs.superuser.label("Admin?") #定义编辑模式下通用的设置,编辑模式包括:新增,修改 def wmode(self, password=None): self.setLabel() #由于新增和修改中都须要用户从新确认密码,因此要为表单加入Confirm Password #若是有指定password的值,说明用户是在修改记录,那么Confirm Password也必须有值 if not password: self.fs.insert_after(self.fs.password, Field("confirm_password")) else: self.fs.insert_after(self.fs.password, Field("confirm_password", value=password)) self.confirm_password = self.fs.confirm_password.label("Re-enter Password") self.name = self.name.required().validate( validators.length(min=6, max=20) ).with_html( minlength_=6,#定义客户端验证限制该字段最小长度为6 class_="{messages:{remote:'该用户名已存在'}}"#定义客户端惟一验证错误的提示语 ) self.email = self.email.required().email().validate( validators.email ).validate( validators.maxlength(32) ).with_html( class_="{messages:{remote:'该邮箱已存在'}}"#定义客户端惟一验证错误的提示语 ) self.password = self.password.required().password().validate( validators.length(min=6, max=32) ).with_html( minlength_=6,#定义客户端验证限制该字段最小长度为6 ) self.confirm_password = self.confirm_password.required().password().validate( validators.length(min=6, max=32) ).validate( customEqual("password", "密码先后不一致") ).with_html( minlength_=6,#定义客户端验证限制该字段最小长度为6 ) #定义新增用户时调用的方法 def write_render(self, cls): #设置Fieldset对象,指定要绑定的sqlalchemy中的表类,并赋予sqlalchemy的session self.fs = FieldSet(User, session=cls.db) self.wmode() #配置表单信息 self.fs.configure( #表单包含的字段 include=[ self.name.validate(customUnique(cls.db)).with_html( remote_="/unique/User/name/create/",#定义客户端远程验证的地址 ), self.email.validate(customUnique(cls.db)).with_html( remote_="/unique/User/email/create/",#定义客户端远程验证的地址 ), self.password, self.confirm_password.with_html( class_="{equalTo:'#User--password'}"#设置客户端验证该字段值与另外一个字段值是否相等 ) ] ) return self.fs修改完成后运行main.py,进入/form/,输入已经注册过的name和email,看看客户端远程验证是否实现了:)