以前写前端表单的时候,通常都是直接把表单里的一个个input写在html文件中,这样写form提交方式是可行的,但是对于表单的默认提交是不具有验证效果,除非把值传到后台再进行处理,这样一来其实还是很麻烦;如果采用ajax提交表单,则都需要对每个需要验证的进行正则匹配,然后判断再发起,也一样麻烦。还有一个缺点就是如果在网站中需要多个同样的表单,那么就需要在前端模板中写多个一模一样的form表单。要是有个form模型在想用的时候实例化一下就可以用该多好?
在flask中的WTF刚好可以解决这一问题,一般是建立一个存放form模型的文件form.py,然后在视图函数里进行实例化,再传到前端模板渲染,这样即便在多个地方需要同一个form表单,直接调用实例即可。用这种form模型不仅大大提高效率,还具有较好的表单验证。在前面的Flask实现登录、注册(邮箱验证)中也有写到关于WTF的表单提交,前面提到的用户注册、登录表单都是基于WTF的form模型创建的,这两个form都是submit默认提交到当前页,本文则是用ajax提交表单。
首先先写个我们需要的表单form模型,即在form.py文件中添加一下代码
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, ValidationError, RadioField, DateField, SelectField
from wtforms.validators import Required, Length, Email, Regexp, EqualTo
from app import label
class EditUserInfoForm(FlaskForm):
"""
# Radio Box类型,单选框,choices里的内容会在ul标签里,里面每个项是(值,显示名)对
gender = RadioField('Gender', choices=[('m', 'Male'), ('f', 'Female')],
validators=[DataRequired()])
"""
name = StringField(label('_user_name'),validators=[Required(),Regexp(r"^[\u4E00-\u9FA5]{2,4}$",
message=label('_input_erro',['真实姓名']))])
sex = RadioField(label('_sex'), coerce=int, choices=[(0, label('_male')), (1, label('_female'))])
nickname = StringField(label('_nickname'),validators=[Required(),Regexp(r'^([A-Za-z]+\s?)*[A-Za-z]$',
message=label('_input_erro',['英文名']))])
email = StringField(label('_email'), validators=[Email(message=label('_input_erro',['邮箱']))])
tel = StringField(label('_tel'),validators=[Required()])
birthday = DateField(label('_birthday'),validators=[Required(message=label('_input_erro',['出生日期']))])
submit = SubmitField(label('_update'))
其中label用于文字的提示函数,我们放在app目录下的__init__.py中初始化,代码如下:
from werkzeug.utils import import_string
def label(name, values=[], path=None):
'''
默认从app目录下的guide导入所需变量
name 导入的数据变量名
values 数据类型必须市列表,提示的动态内容
path 为导入的路径
'''
path = ".".join(path.split(".")[:-1]) + ".guide:" if path else "app.guide:"
msg = import_string(path + name)
return msg.format(values) if len(values) else msg
并在同级目录下创建存放我们想要引用的文字或者字符(guide.py),其中命名自定义,代码如下:
_update_succ = '''更新成功'''
_upload_succ = '''上传成功'''
_sex = '''性别'''
_male = '''男'''
_female = '''女'''
_user_name = '''真实姓名'''
_nickname = '''英文名'''
_email = '''邮箱'''
_tel = '''电话'''
_birthday = '''出生日期'''
_update = '''更新'''
_change_succ = '''success'''
_change_erro = '''error'''
_input_erro ='''{0[0]}格式有误'''
当我们想调用(没)有占位符的时候,直接导入label,然后在指定的位置使用label函数,例如:
#无占位符
sex = RadioField(label('_sex'), coerce=int, choices=[(0, label('_male')), (1, label('_female'))])
#有占位符
name = StringField(label('_user_name'),validators=[Required(),Regexp(r"^[\u4E00-\u9FA5]{2,4}$",
message=label('_input_erro',['真实姓名']))])
form模型建好之后,就是视图函数,实例化form表单,然后传到前端模板渲染,代码如下:
@auth.route('/information')
@login_required
def show_info():
#实例化表单的值就是给表单填写默认值
form = EditUserInfoForm(name=current_user.name,nickname=current_user.nickname,\
email=current_user.email,tel=current_user.tel,birthday=current_user.birthday,sex=current_user.sex)
return render_template('auth/users/information.html' form=form)
前端模板如下:
<form action="#" id="changeform" method="POST">
{{ form.hidden_tag() }}
<div class="form-body overflow-hide">
<div class="form-group">
<label class="control-label mb-10" for="exampleInputuname_1">{{ form.name.label }}</label>
<div class="input-group">
{{ form.name(class="form-control", id='name', required="required") }}
</div>
</div>
<div class="form-group">
<label class="control-label mb-10">{{ form.sex.label }}</label>
<div class="radio">
{{ form.sex(required="required",class='sex-radio') }}
</div>
</div>
<div class="form-group">
<label class="control-label mb-10" for="exampleInputEmail_1">{{ form.nickname.label }}</label>
<div class="input-group">
{{ form.nickname(class="form-control", id='nickname', required="required") }}
</div>
</div>
<div class="form-group">
<label class="control-label mb-10" for="exampleInputuname_1">{{ form.email.label }}</label>
<div class="input-group">
{{ form.email(class="form-control", id='email', required="required") }}
</div>
</div>
<div class="form-group">
<label class="control-label mb-10" for="exampleInputuname_1">{{ form.tel.label }}</label>
<div class="input-group">
{{ form.tel(class="form-control",id='tel', required="required") }}
</div>
</div>
<div class="form-group">
<label class="control-label mb-10" for="exampleInputuname_1">{{ form.birthday.label }}</label>
<div class="input-group">
{{ form.birthday(class="form-control", id='birthday') }}
</div>
</div>
</div>
<div class="modal-footer">
{{ form.submit(id="submit_btn", class="btn btn-success waves-effect", required="required") }}
<button type="button" class="btn btn-default waves-effect" data-dismiss="modal">取消</button>
</div>
</form>
jq的Ajax代码如下:
$(document).ready(function(){
$('#submit_btn').on('click',function(e){
//该命令时取消表单的默认提交
//防止默认提交后跳转
e.preventDefault()
//用POST方法提交,把整个form提交到后台
var post_data=$('#changeform').serialize();
$.ajax({
url:'updateinfo',
method:'POST',
dataType:'json',//后台返回json格式的返回值
data:post_data,
success:function(response){
//如果想把后台返回回来的json对象转字符
//用JSON.stringify(response)转字符
for (var key in response){
// alert(response[key]);
console.log(response[key])
}
})
});
});
});
后台请求接口代码如下:
@auth.route('/updateinfo', methods=['POST'])
@login_required
def update_info():
'''
ajax保存用户的修改信息
'''
name=request.form.get('name')
nickname=request.form.get('nickname')
sex=request.form.get('sex')
email=request.form.get('email')
tel=request.form.get('tel')
birthday=request.form.get('birthday')
form = EditUserInfoForm(name=name,nickname=nickname,email=email,tel=tel,birthday=birthday,sex=sex)
if form.validate_on_submit():
current_user.name=name
current_user.nickname=nickname
current_user.sex=int(sex)
current_user.email=email
current_user.tel=tel
current_user.birthday=birthday
db.session.add(current_user)
result = {'success':label('_update_succ')}
else:
result = form.errors
return json.dumps(result)
一开始form.validate_on_submit()不停的返回false,后来经过调试,才发现是因为在建立form模型的,对于性别的RadioField缺少一个属性,导致到后台始终不被识别,后来找到资料,在RadioField里面添加一个coerce属性才能运行成功。
sex = RadioField(label('_sex'), coerce=int, choices=[(0, label('_male')), (1, label('_female'))])
写代码还是要多总结多整理,不然下次再遇到这种很难找的错,估计又要折腾老半天才能解决。