对于python的web轻量级框架flask来说,他是没有包含表单验证的,所以当我们处理复杂表单验证的时候会很麻烦,这时候就需要使用flask为我们推荐的第三方轮子wtform。
这里有一个flask-wtform就跟flask-bootstrap,flask-sqlalchemy一样,将wtform的一些内容更改变得更加适配web框架,减少了一些不必要的功能。
安装
使用pip进行安装wtform
pip3 install flask-wtform
快速开始
你可以定义一个继承FlaskForm的类
class UserForm(FlaskForm):
name=stringField('name',validators=[])
他还有下图的这些Field。
上面的stringField其实就是你的表单里的text文本框。参数的那么指的是你的表单text的名字,而validators就是校验链了。我们可以点进wtforms源码看一看。
这些判断全部可以放在validators.
__all__ = (
"DataRequired",
"data_required",
"Email",
"email",
"EqualTo",
"equal_to",
"IPAddress",
"ip_address",
"InputRequired",
"input_required",
"Length",
"length",
"NumberRange",
"number_range",
"Optional",
"optional",
"Regexp",
"regexp",
"URL",
"url",
"AnyOf",
"any_of",
"NoneOf",
"none_of",
"MacAddress",
"mac_address",
"UUID",
"ValidationError",
"StopValidation",
)
假如我们要写一个用户名的判断。
username = StringField('username', validators=[data_required,
length(min=4, max=8,message='你的用户名必须大于4个字符,小于8个字符!')])
如果是一个密码的判断呢
password=PasswordField('password',validators=[data_required,length(min=4, max=8,
message='你的密码必须大于4个字符,小于8个字符!')])
前端页面使用
你上面已经创建了wtform的表单类了,接下来将他返回给前端。
def user_login():
user_form = UserForm()
return render_template("user/login.html",user_form=user_form)
然后前端进行显示
<form method="POST" action="/">
{{user_form.password}}
<input type="submit" value="Go">
</form>
就上面的password属性,他实际上就会变成表单标签input[password]。
这里我们就完成了基本的wtform使用。
后台使用
创建user_form使用方法validate_on_submit() 来进行validate chain的判断,将所有错误存储到每个字段的erros列表中返回。
user_form=UserForm()
if user_form.validate_on_submit() is not True:
return render_template("user/login.html",user_form=user_form)
CSRF
在我们运行的时候,有一个小插曲,他会爆csrf的错误,实际上,flask-wtform为我们封装了csrf protection,任何一个使用了FlaskForm的视图都会提前声明一个csrf protect,如果你使用的是ajax请求,他就不会去声明csrf保护,可以看到下面源码,他是去获取我们配置里面的secret_key。
所以我们在配置中声明secret_key
app.config['SECRET_KEY']='sdfgwfjoi23iosjdfi2'
介绍
csrf是什么呢,实际上csrf是一种web攻击,csrf全称是(Cross-site request forgery)跨站请求伪造,也就是跨域问题。他是一种对网站的恶意利用。
简单来说,就是攻击者通过一些技术手段,来使用已经有你的登录信息的浏览器进行访问并进行一系列操作。浏览器只能识别真实用户的浏览器,但并不能识别是不是真正的用户在操作浏览器。
自定义判断
在flask_wtform的实体类中新增方法,validate_<field_name>
def validate_password(self,field):
if self.data != '123':
raise ValidationError('密码错误')
self为Form表单对象,field为html标签对象。可以用field.data获取数据进行判断。发生错误之后返回ValidationError的异常。将error
自定义标签
上面我们演示的只是基础的出现html标签,那我们需要它的id或者class,或者增加新的属性怎么办。
我们可以用render_kw
password=PasswordField('password',render_kw={'class':'login-input','placeholder':'请输入密码'},
validators=[data_required()])
文件上传
两个validation就足够文件上传,如果还想加声明
class FileForm(FlaskForm):
avatar=FileField('image', validators=[
FileRequired(),
FileAllowed(['jpg', 'png'], '只能上传jpg和png后缀图片!')
])
captcha
就是真人验证,判断你是不是真人。如何使用呢?
在你的flaskform中引用:
class UserForm(FlaskForm):
recaptcha = RecaptchaField()
前端页面
{{ user_form.recaptcha }}
真实页面就会被注入
可以看到引入了一个js,一个div。但是因为我们在国内,网络无法访问google的api.js,所以无法使用这个。所以我们可以用pillow自己做一个图形验证码。
自定义验证码
建一个工具类
# 创建验证码工具类
import random
from PIL import Image, ImageFont, ImageDraw, ImageFilter
from apps.settings import Config
def get_random_color():
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
return color
def create_captcha(length):
size = (length * 25, 50)
image = Image.new('RGB', size, get_random_color())
# 获取ttf字体文件
font = ImageFont.truetype(Config.BASH_DIR.join('resources/') + 'consola.ttf', size=40)
# 将要从这里选择内容
random_str = 'QWERTYqwertyASDFGasdfguioZXCVBzxcvbnm1234567890'
# 真正的验证码
real_str = ''
draw = ImageDraw.Draw(image, 'RGB')
for i in range(length):
choice = random.choice(random_str)
real_str += choice
# 设置干扰线xy轴
x0 = random.randint(0, 3)
y0 = random.randint(0, 50)
# x1 = random.randint(image.size[0]/2,image.size[0])
y1 = random.randint(0, 50)
# 设置干扰线
draw.line([(x0, y0), (image.size[0], y1)], fill=get_random_color())
# 设置每一个随机字
draw.text(text=choice, fill=get_random_color(), font=font,
xy=(random.randint(5, 7) + i * 20, random.randint(3, 5)))
#设置滤镜
image=image.filter(ImageFilter.EDGE_ENHANCE)
return image
将flaskform中的recaptchaField改成string类型,并且将我们自定义的验证码发送到前端。
# 获取图形验证码
@user_bp.route("/get_captcha",methods=['get'])
def get_captcha():
captcha = create_captcha(length=4)
img_io = BytesIO()
captcha.save(img_io, 'PNG')
img_io.seek(0)
# 使用flask自带的方法 send_file
return send_file(img_io, mimetype='image/png')
前端直接地址接收就好。因为他直接返回二进制文件。
<img src='localhost:8080/user/get_captcha'/>
这个时候我们只完成了显示,我们还需要实现点击更换验证码。其实只需要通过js在后面加上一个随机数,让服务器知道这是另一个链接就好了。
function captcha(){
var img=document.getElementById('captcha')
img.src='/user/get_captcha?ra='+Math.random()
}