一、自定义Form的原理

1.1 各种form表单验证比较

只有python提供了form表单验证,其他的都没有提供。django提供的功能还不够强大。最强大的是微软的ASP.NET!我们可以自己写一个来实现。

1.2 【不推荐】通过request.post获取用户输入内容

用户输入表单==>用户输入的检测

1)我们之前是创建一个form类

2)用户提交表单,我们通过request.post拿到数据,然后封装到Form(数据)里面

3)obj.is_valid方法,来检查用户输入的内容,跟Form()定义的,是否匹配。

问题:

request.post 获取用户输入的内容,它知道用户输入了几个吗?

1.3 【推荐】通过自定义Form类,通过类对象来获取

1.3.1如何获取类的所有静态字段:

所以,通过request.post取数据不好,所以我们可以用form类。

如:

Form类:

u = xxxx

p = xxxx

我们先创建一个Form对象:

obj = Form()

for i in Form类中的所有东西:

问题:

Form类:这些都是静态字段,静态字段属于类。

如何获取一个类的所有静态字段?

'''

这些静态属性是属于类的

这就是通过打印类的字典,来获取类的静态属性

'''

class Foo(object):

p=123

u=456

print Foo.__dict__

# 如果要循环就是循环类的所有东西:

# for k,v in Foo类的所有东西:

'''

打印结果:

{'__module__': '__main__', 'p': 123, 'u': 456, '__dict__':

<attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}

'''

1.3.2 通过类对象来获取所有实例属性

'''

如果把属性写到初始化里,我们要找这些属性的话,就要找类的对象的字典了(这些属性属于类的实例)

'''

class Foo2(object):

def __init__(self):

self.u='wang'

self.p=123

# 如果要循环就是循环类的对象所有东西:

# for k,v in Foo2对象所有东西:

obj=Foo2()

print obj.__dict__

# 打印结果

# {'p': 123, 'u': wang}

# for k,v in 对象的所有东西:

1.3.3 自定义form验证原理(类对象获取对象属性)

'''

我们先定义一个类,类里面有多少字段,是我们程序员控制的。

我们可以根据Foo类来生成页面的标签!

用户提交数据的时候,我们先创建Foo类对象,然后再循环对象里所有的东西。

'''

#这里的obj为类的实例化对象

for k,v in obj.__dict__.iteritems():

print k,v   # k代表属性名:如'p', v代表值:如:123

# request.POST[k] # 如果循环,来获取request.POST[k],其实就是获取用户输入的数据

# 如果用户输入的是alex,request.POST[k]就等于alex

# 所以,我们通过循环自己的form来去前端取什么数据。是以我们在Form定义的项目为主,跟前端写多少没关系。

二、自定义Form实例1

2.1 获取用户输入的值

就是创建一个web程序,只要请求一进来,访问/index,其实就是访问MainHandler类的get方法或者post方法

目录结构如下:

运行:form_framework.py

浏览器访问:http://localhost:8888/index

这时,就执行了MainHandler类的get方法,就是渲染index.html页面

class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html') def post(self, *args, **kwargs):
host = self.get_argument('host') # 在Tornado里,获得用户的输入,都是用get_argument
print host

  

重新运行:form_framework.py

然后输入:hostname内容,然后提交

此时python后台就打印:wang

2.2 自定义form类,打印对象的k,v

既然能获取单个属性,我们就可以循环form类对象来获取对象的所有属性(用户输入数据)

我们定义一个Form类,然后定义属性名(注意,跟form表单的name名要对应)

然后循环

class Form(object):
def __init__(self):
self.host = None
self.ip = None
self.port = None
self.phone = None class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html') def post(self, *args, **kwargs):
# host = self.get_argument('host') # 在Tornado里,获得用户的输入,都是用get_argument
# print host
obj=Form()
for k,v in obj.__dict__.iteritems():
print k,v

现在后台打印:

ip None

host None

port None

phone None

我们可以通过自定义的form来获取用户提交的数据,如果index.html,多了一个地址栏,

<p>address: <input type="text" name="address" /> </p>

但是form类里没定义,我们也不管它。

也就是我们只收集form类定义的对象属性

加上:self.get_argument(k)

def post(self, *args, **kwargs):
# host = self.get_argument('host') # 在Tornado里,获得用户的输入,都是用get_argument
# print host
obj=Form()
for k,v in obj.__dict__.iteritems(): print k,v,self.get_argument(k) # 对象的k和值,用户输入的值(通过跟form表单里的name名对应)

此时再从浏览器输入表单内容,提交

python后台打印结果:

ip None 10.0.0.1

host None wang

port None 22

phone None 123456

没有address,因为我们不管它。这样就做到了,我们在Form类里写了哪些字段,就取用户输入的哪些数据。

2.3 用正则来验证用户输入数据

我们为啥要写Form类呢?因为要做验证!

k是字段,v是form对象的值,self.get_argument(k)是用户输入的值。

我们把v改成正则表达式,我们用正则来验证用户输入的值,如果验证成功,就代表合法的,不成功就不合法。

1)我们先改写Form类,把值改成正则表达式:

class Form(object):
def __init__(self):
self.host = "(.*)"
self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
self.port = '(\d+)'
self.phone = '^1[3|4|5|8][0-9]\d{8}$'

然后我们,通过正则验证,如果循环过程,有一个验证不成功,flag=False,打印错误:

def post(self, *args, **kwargs):
# 在请求的时候,默认是验证成功的
flag= True
# host = self.get_argument('host') # 在Tornado里,获得用户的输入,都是用get_argument
# print host
obj=Form()
for k,v in obj.__dict__.iteritems():
# k,对象的字典
# v,对象中字段对应的值,正则
# self.get_argument(k),用户输入的值(通过跟form表单里的name名对应)
# 我们用正则来验证用户输入的值,如果验证成功,就代表合法的,不成功就不合法。
# print k,v,self.get_argument(k)
import re
if re.match(v,self.get_argument(k)): # 如果符合,则返回一个对象,如果不符合就返回一个None
pass
else:
flag = False # 在循环的过程中,一旦有一个不满足,就是false了。 if flag:
self.write('ok')
else:
self.write('error')

  

重启:form_framework.py

然后在表单输入错误的格式,提交后就返回error:

输入格式错误,返回:error

2.4 把验证写到form类里

因为必须从Form类里获取正则表达式,再做验证,所以,我们直接把验证写到Form类里,把MainHandler类的post只做主函数就行了。

1)首先把:or k,v in obj.__dict__.iteritems(): 移动到Form里,

这里的obj,就是Form的实例化对象,在Form里就是指self

class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html') def post(self, *args, **kwargs):
# 在请求的时候,默认是验证成功的
flag= True
obj=Form()
#for k,v in obj.__dict__.iteritems(): #移动到Form类里
import re
if re.match(v,self.get_argument(k)): pass
else:
flag = False # 在循环的过程中,一旦有一个不满足,就是false了。 if flag:
self.write('ok')
else:
self.write('error') class Form(object):
def __init__(self):
self.host = "(.*)"
self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
self.port = '(\d+)'
self.phone = '^1[3|4|5|8][0-9]\d{8}$'
def is_valid(self):
for k,v in self.__dict__.iteritems(): # 移动到这里,obj改成self
import re

2)传代码:if re.match(v,self.get_argument(k)):

看上面代码

if re.match(v,self.get_argument(k)):

post函数里的self,指的是MainHandler这个类的对象,

我们把is_valid函数传个参数,request,

在is_valid函数里改写代码为:if re.match(v,request.get_argument(k)):

其实这里的request就是:MainHandler对象,

is_valid函数在MainHandler里被调用,把自己的对象self传进去。

3)把flag=True,分别定义在两个函数里

改写后的代码如下:

class Form(object):
def __init__(self):
self.host = "(.*)"
self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
self.port = '(\d+)'
self.phone = '^1[3|4|5|8][0-9]\d{8}$'
def is_valid(self):
# 在请求的时候,默认是验证成功的
flag= True
for k,v in self.__dict__.iteritems():
import re
import re
if re.match(v,self.get_argument(k)): # 如果符合,则返回一个对象,如果不符合就返回一个None
pass
else:
flag = False # 在循环的过程中,一旦有一个不满足,就是false了。 class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html') def post(self, *args, **kwargs):
# host = self.get_argument('host') # 在Tornado里,获得用户的输入,都是用get_argument
# print host
flag= True
obj=Form()
obj.is_valid(self) # 把自己的类对象self传到MainHandler里。
if flag:
self.write('ok')
else:
self.write('error')

三、Form类优化

3.1 把is_valid放到基类里

在django里,每一个表单就是一个form,例如LoginForm,AssetForm。。。

在你创建很多form的时候,但是is_valid都一样。你需要在每个Form类里都写一遍吗?

不需要,你可以用基类。

class BaseForm(object):
def is_valid(self):
# 在请求的时候,默认是验证成功的
flag= True
for k,v in self.__dict__.iteritems():
import re
import re
if re.match(v,self.get_argument(k)): # 如果符合,则返回一个对象,如果不符合就返回一个None
pass
else:
flag = False # 在循环的过程中,一旦有一个不满足,就是false了。 class Form(BaseForm):
def __init__(self):
self.host = "(.*)"
self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
self.port = '(\d+)'
self.phone = '^1[3|4|5|8][0-9]\d{8}$' class LoginForm(object):
def __init__(self):
self.name= "(.*)"

3.2 正则表达式分门别类

如果多个表单有同一个正则,例如ip地址,就会重复写同样正则了。

我们可以针对每个类型的验证,单独写正则表达式类。

调用的时候,调用这个字段的正则类的对象就行了。这样就提高重用性了。

class ValidateIpv4(object):
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') class ValidatePhone(object):
phone_re = re.compile(r'^1[3|4|5|8][0-9]\d{8}$') class Form(BaseForm):
def __init__(self):
self.host = "(.*)"
self.ip = ValidateIpv4.ipv4_re
self.port = '(\d+)'
self.phone = ValidatePhone.phone_re

  

  

3.3 验证后返回成功或失败信息

在django里,验证无论成功或者失败,都返回信息

对于IP来说,如果IP错误的话,也会有IP格式错误的提示。

在django中是这么做的:

from django import forms

import re
from django.core.exceptions import ValidationError def validate_ipv4(value): # ip地址 验证例如:10.1.6.10
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
if not ipv4_re.match(value): # 这里做了验证,如果不成功,则报错
raise ValidationError('IP段格式错误.') # ValidationError是django的方法 class AddressPoolForm(forms.Form):
getway = forms.CharField(validators=[validate_ipv4, ], # 验证ip地址
error_messages={'required': u'网关不能为空', 'invalid': u'网关格式错误'},
widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': '网关'}))

那在自定义的Form里怎么做呢?

那在自定义的Form里怎么做呢?

required的值是传进来的:

required:True  表示:字段必须要填,并且验证

required:False  表示:字段可填可不填,不做验证

假如,required是False,则不做验证,直接返回值。

如果required=True:则判断:如果没有自定义'required'和'valid'错误信息,则用默认的(Field提供)

如果验证成功,则返回正则匹配的内容。

import tornado.ioloop
import tornado.web
import re class Field(object):
def __init__(self, error_msg_dict, required):
self.id_valid = False
self.value = None
self.error = None
self.name = None
self.error_msg = error_msg_dict
self.required = required def match(self, name, value):
self.name = name if not self.required: # 假如,required是False,则不做验证,直接返回值。
self.id_valid = True
self.value = value
else:
if not value: # 假如用户没传值,则给出require报错信息:
if self.error_msg.get('required', None): # 先看有没有自定义的'required'错误信息,有就用自定义的,没有则用默认的。
self.error = self.error_msg['required']
else:
self.error = "%s is required" % name
else: # 如果用户传来值,则做验证,如果验证成功,则返回匹配结果,没成功,则返回自定义(优先级高)或默认信息
ret = re.match(self.REGULAR, value)
if ret:
self.id_valid = True # 是正则验证成功的标识
self.value = ret.group()
else: # 先看有没有自定义的'valid'错误信息,有就用自定义的,没有则用默认的。
if self.error_msg.get('valid', None):
self.error = self.error_msg['valid']
else:
self.error = "%s is invalid" % name # 如果required=True:则判断:如果有自定义了'required'和'valid'错误,则用自定义的
class IPField(Field):
REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$" def __init__(self, error_msg_dict=None, required=True):
error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
if error_msg_dict:
error_msg.update(error_msg_dict)
# 继承基类的__init__方法,同时,把参数(error_msg,required)传给基类__init__
super(IPField, self).__init__(error_msg_dict=error_msg, required=required)

说明:

class IPField(Field):里的:

# 外面传参:error_msg_dict 可以定制化错误信息!这里required默认值是True

def __init__(self, error_msg_dict=None, required=True):

        error_msg = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'} 

        if error_msg_dict:

            error_msg.update(error_msg_dict) # 把定制化的错误信息来更新error_msg这个字典(也就是定制化错误优先级最高!)

# 这时候调用IPField,就可以随意传自定义的错误信息了!required=True是默认值,可以不用传

class Form(BaseForm):
def __init__(self):
# required=True是默认值,可以不用传
self.ip = IPField(error_msg_dict={'required': '我的IP不能为空', 'valid': '我的IP格式错误'})

最新文章

  1. JS将秒转换为 天-时-分-秒
  2. ios第三方开源库
  3. redis基本数据类型【2】-Hash类型
  4. 让盘古分词支持最新的Lucene.Net 3.0.3
  5. Unknown
  6. 每篇半小时1天入门MongoDB——4.MongoDB索引介绍及数据库命令操作
  7. [HNOI2013]游走
  8. 【codeforces 698B】 Fix a Tree
  9. (Python)PO设计模式
  10. stroop效应matlab实验
  11. 初识Scala
  12. David Silver强化学习Lecture1:强化学习简介
  13. mybatis中Parameter index out of range (1 &gt; number of parameters, which is 0).
  14. C++通过jsoncpp类库读写JSON文件-json用法详解
  15. spring 中 PO与DTO相互转换的工具类
  16. ConfigUtil读取配置文件
  17. 【转载】Linux内存中buffer和 cached的比较
  18. hadoop应用场景总结
  19. hdu1158 Employment Planning 2016-09-11 15:14 33人阅读 评论(0) 收藏
  20. 前台js加密实例

热门文章

  1. CentOS7布署.Net Core
  2. NLayerAppV3-Infrastructure(基础结构层)的Data部分和Application(应用层)
  3. c# WPF客户端调用WebAPI并转换成List
  4. Mac OS 10.12 - 如何关闭Rootless机制?
  5. poj3070矩阵快速幂求斐波那契数列
  6. Educational Codeforces Round 25 C. Multi-judge Solving
  7. activemq消息生产者与消息消费者简单例子
  8. NSCache 的好处
  9. python学习笔记07-元组 字典
  10. 自动安装zabbix_agent脚本 -- python2