web.py开发web 第九章 Formalchemy 客户端验证

    上一章讲了使用formalchemy进行服务端验证,但要开发一个用户体验良好的网站,客户端验证是必不可少的,用户可以在填写的时候就获取到错误信息,不必等待提交的过程,大大节省了用户的时间。

    Formalchemy进行客户端验证,其实就是使用前端的js插件定义验证方法,然后在formalchemy中为字段赋予特殊的属性,让验证插件从属性了解某个字段需要进行什么验证,例如,我们在User表中定义email字段必须为email形式数据,那么formalchemy会在输出的表单中设置email字段的type为email,前台使用的插件能识别type="email"的input,然后进行email格式验证。

    这里介绍一个我常用的jquery验证插件validation,这个插件本身已内置了很多常用的验证方法,如果有特殊的验证需求,还可以进行自定义,有兴趣的同学可以谷歌下。下面我们就以jquery的validation为验证插件,结合formalchemy来介绍下如何进行客户端验证。

    首先我们下载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文件,代码如下
base.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表单的验证需求:


  1. name字段必填且数据库内唯一,长度不小于6,不大于20。
  2. email字段必填且数据库内唯一,且为email格式,长度不大于32。
  3. password必填,长度不小于6,不大于32。
  4. confirm password必填且必须与password相等,长度不小于6,不大于32。
    根据需求,我们对name和email的唯一验证需要使用到远程验证,即需要中创建验证唯一的视图,所以我们在main.py创建一个unique的类来实现这个需求,将以下unique类添加进main.py中:


class unique

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修改如下:

forms.py

#-*- 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,看看客户端远程验证是否实现了:)


转载于:https://my.oschina.net/zhengnazhi/blog/123471

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值