上一章講了使用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,看看客戶端遠程驗證是否實現了:)