flask_八、表单+表单提交、csrf、表单渲染
表单相关处理
WTForms支持在python中使用类定义表单,然后通过类定义生成对应的HTML代码,这种方式更方便,易于重用。因此,除非是非常简单的程序,或者想让表单的定义更加灵活,否则不会在模板中直接使用HTML编写表单。
一、使用Flask-WTF处理表单
扩展Flask-WTF继承了WTFforms,使用它可以在flask中更方便的使用WTForms。Flask-WTF将表单数据解析、CSRF保护、文件上传等功能与Flask集成。
二、定义WTForms表单
2.1在Template文件夹下新建一个htmlform.html的文件
<form method="post"> <label for="usernamne">Username</label><br> <input type="text" name="username" placeholder="Ross Gellar"><br> <label for="password">Password</label><br> <input type="password" name="password" placeholder="20201013"><br> <input id="remember" name="remember" type="checkbox" checked> <label for="remember"><small>Remember me</small></label><br> <input type="submit" name="submit" value="log in"> </form>
2.2在最外层新建form.py(根路径下)
# encoding=utf-8 # from flask_wtf import FlaskForm from wtforms import Form,StringField,PasswordField,BooleanField,SubmitField from wtforms.validators import DataRequired,Length class LoginForm(Form): username = StringField("Username",validators=[DataRequired()]) password = PasswordField("Password",validators=[DataRequired()]) remember = BooleanField("Remember me") submit = SubmitField("Log in")
2.3前置条件:启动flask之后,安装flask_wtf
1) pipenv shell
2) pipenv install flask_wtf

2.4输出HTML代码
E:\flask_study>py -3 Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> from wtforms import Form,StringField,PasswordField,BooleanField,SubmitField >>> from wtforms.validators import DataRequired,length >>> class LoginForm(Form): ... username = StringField("Username",validators=[DataRequired()]) ... password = PasswordField("Password",validators=[DataRequired()]) ... remember = BooleanField("Remember me") ... submit = SubmitField("Log in")

>>> form = LoginForm() #实例化 >>> form.username #调字段属性 <wtforms.fields.core.StringField object at 0x000002281B944D30> >>> form.username() '<input id="username" name="username" required type="text" value="">' >>> form.password() '<input id="password" name="password" required type="password" value="">' >>> form.remember() '<input id="remember" name="remember" type="checkbox" value="y">' >>> form.submit() '<input id="submit" name="submit" type="submit" value="Log in">' >>> form.username.label


Label('username', 'Username') >>> form.username.label() '<label for="username">Username</label>' >>> form.submit.label Label('submit', 'Log in') >>> form.submit.label() '<label for="submit">Log in</label>' >>>
|
字段类 |
说明 |
对应的HTML表示 |
|
BooleanField |
复选框,值会被处理为True或False |
<input type=”checkbox”> |
|
DateField |
文本字段,值会被处理为datetime.date对象 |
<input type=”text”> |
|
DateTimeField |
文本字段,值会被处理为datetime.datetime对象 |
<input type=”text”> |
|
FileField |
文件上传字段 |
<input type=”file”> |
|
FloatField |
浮点数字段,值会被处理为浮点型 |
<input type=”text”> |
|
IntegerField |
整数字段,值会被处理为整型 |
<input type=”text”> |
|
RadioField |
一组单选按钮 |
<input type=”radio”> |
|
selectField |
下拉列表 |
<select><option></option></select> |
|
SelectMultipleField |
多选下拉列表 |
<select multiple><option></option></select> |
|
SubmitField |
提交按钮 |
<input type=”submit”> |
|
StringField |
文本字段 |
<input type=”text”> |
|
HiddenField |
隐藏文本字段 |
<input type=”hidden”> |
|
PasswordField |
密码文本字段 |
<input type=”password”> |
|
TextAreaField |
多行文本字段 |
<textarea></textarea> |
三、字段类构造方法接受的常用参数
通过实例化字段类时传入的参数,我们可以对字段进行设置,字段类构造方法接受的常用参数如下表:
|
参数 |
说明 |
|
label |
字段标签<label>的值,也就是渲染后显示在输入字段前的文字 |
|
render_kw |
一个字典,用来设置对应的HTML <input>标签的属性,比如传入{‘placeholder’: ‘Your Name’},渲染后的HTML代码会将<input>标签的placeholder属性设为YourName |
|
validators |
一个列表,包含一系列验证器,会在表单提交后被逐一调用验证表单数据 |
|
default |
字符串或可调用对象,用来为表单字段设置默认值 |
四、常用的验证器
在WTForms中,验证器(validator)是一系列用于验证字段数据的类,我们在实例化字段类时使用validators关键字来指定附加的验证器列表。
验证器从wtforms.validators模块中导入,常用的验证器,如下表:
|
验证器 |
说明 |
|
DataRequired(message=None) |
验证数据是否有效 |
|
Email(message=None) |
验证Email地址 |
|
EqualTo(filename, message=None) |
验证两个字段值是否相同 |
|
InputRequired(message=None) |
验证是否有数据 |
|
Length(min=-1, max=-1, message=None) |
验证输入值长度是否在给定范围内 |
|
NumberRange(min=None, max=None, message=None) |
验证输入数字是否在给定范围内 |
|
Optional(strip_whitespace=True) |
允许输入值为空,并跳过其他验证 |
|
Regexp(regex, flags=0, message=None) |
使用正则表达式验证输入值 |
|
URL(require_tld=True, message=None) |
验证URL |
|
AnyOf(values, message=None, values_formatter=None) |
确保输入值在可选值列表中 |
|
NoneOf(values, message=None, values_formatter=None) |
确保输入值不在可选值列表中 |
五、示例
5.1更新form.py
# encoding=utf-8 from flask_wtf import FlaskForm from wtforms import Form, StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Length class LoginForm(FlaskForm): username = StringField("Username", validators=[DataRequired()]) password = PasswordField("Password", validators=[DataRequired()]) remember = BooleanField("Remember me") submit = SubmitField("Log in")
5.2再次更新form.py的内容
# encoding=utf-8 from flask_wtf import FlaskForm from wtforms import Form, StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Length class LoginForm(FlaskForm): username = StringField("validate_Username", render_kw={'placeholder':'Your Username'}) password = PasswordField("Password", validators=[DataRequired(),Length(8,128)]) remember = BooleanField("Remember me") submit = SubmitField("Log in")
5.3实例化的时候会报错(如下)
form = LoginForm()

5.3.1解决:(激活上下文)
>>> from app import app
>>> app.test_request_context("/html")
<RequestContext 'http://localhost/html' [GET] of app>
>>>
>>>
>>> app.test_request_context('/html').push()
5.3.2问题解决后的效果

有CSRF的保护处理(flask_wtf需要)
来判断请求是否来自自己的网站
跨站请求伪造,生成令牌token
进行令牌保护

六、Csrf处理的例子(传入表单类实例)
6.1app.py文件内容
# 传入表单类实例 # encoding=utf-8 from flask import Flask,render_template,flash import os from form import LoginForm from flask import request app = Flask(__name__) app.secret_key = os.getenv('SECRET_KEY', 'secret string') app.config['WTF_CSRF_ENABLED'] = False @app.route('/form',methods=['GET','POST']) def basic(): form=LoginForm() return render_template('htmlform.html',form=form) @app.route('/bootstrap',methods=['GET','POST']) def bootstrap(): print("request: %s" % request) print("request.url: %s" % request.url) print("request.form: %s" % request.form) print("request.data: %s" % request.data) print("request.headers: %s" % request.headers) if __name__ == "__main__": app.run(debug=True)
6.2htmlform.html文件内容
<form method="post"> <label for="usernamne">Username</label><br> <input type="text" name="username" placeholder="Ross Gellar"><br> <label for="password">Password</label><br> <input type="password" name="password" placeholder="20201013"><br> <input id="remember" name="remember" type="checkbox" checked> <label for="remember"><small>Remember me</small></label><br> <input type="submit" name="submit" value="log in"> </form>
6.3form.py文件内容
# encoding=utf-8 from flask_wtf import FlaskForm from wtforms import Form, StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Length class LoginForm(FlaskForm): username = StringField("validate_Username", render_kw={'placeholder':'Your Username'}) password = PasswordField("Password", validators=[DataRequired(),Length(8,128)]) remember = BooleanField("Remember me") submit = SubmitField("Log in")
6.4运行结果

七、Csrf生效的例子
7.1新建bootstrap.html文件
内容如下(继承于baseform.html)
{% extends 'baseForm.html'%} {% block styles %} <link rel="stylesheet" href="{{ url_for('static',filename='style.css')}}"> {% endblock %} {% block content %} <h1 class='display-4'>Bootstrap Style</h1> <form method="post"> {{ form.csrf_token }} <div class="form-group"> {{ form.username.label }} {{form.username(class='form-control',required='')}} </div> <div class="form-group"> {{ form.password.label }} {{ form.password(class='form-control') }} </div>
7.2更新htmlform.html
<form method='post'> {{ form.csrf_token }}<!-- 渲染CSRF令牌隐藏字段 --> {{ form.username.label }}{{ form.username }}<br> {{ form.password.label }}{{ form.password }}<br> {{ form.remember }}{{ form.remember.label }}<br> {{ form.submit }}<br> </form>
7.3App.py更新
# 传入表单类实例 # encoding=utf-8 from flask import Flask,render_template,flash import os from form import LoginForm from flask import request app = Flask(__name__) app.secret_key = os.getenv('SECRET_KEY', 'secret string') app.config['WTF_CSRF_ENABLED'] = False # 该值默认是true @app.route('/form',methods=['GET','POST']) def basic(): form=LoginForm() return render_template('htmlform.html',form=form) @app.route('/bootstrap',methods=['GET','POST']) def bootstrap(): print("request: %s" % request) print("request.url: %s" % request.url) print("request.form: %s" % request.form) print("request.data: %s" % request.data) print("request.headers: %s" % request.headers) form = LoginForm() return render_template('bootstrap.html',form=form) if __name__ == "__main__": app.run(debug=True)
7.4运行结果(form)

7.5源码效果

7.6App.py注释掉secret_key的效果

7.7网页源码的效果

八、在模板中渲染表单——用Bootstrap风格
8.1App.py文件内容
# 传入表单类实例 # encoding=utf-8 from flask import Flask,render_template,flash import os from form import LoginForm from flask import request app = Flask(__name__) app.secret_key = os.getenv('SECRET_KEY', 'secret string') # app.config['WTF_CSRF_ENABLED'] = False @app.route('/form',methods=['GET','POST']) def basic(): form=LoginForm() return render_template('htmlform.html',form=form) @app.route('/bootstrap',methods=['GET','POST']) def bootstrap(): print("request: %s" % request) print("request.url: %s" % request.url) print("request.form: %s" % request.form) print("request.data: %s" % request.data) print("request.headers: %s" % request.headers) form = LoginForm() return render_template('bootstrap.html',form=form) if __name__ == "__main__": app.run(debug=True)
8.2Template下的htmlform.html
<form method='post'> {{ form.csrf_token }}<!-- 渲染CSRF令牌隐藏字段 --> {{ form.username.label }}{{ form.username }}<br> {{ form.password.label }}{{ form.password }}<br> {{ form.remember }}{{ form.remember.label }}<br> {{ form.submit }}<br> </form>
8.3template下的bootstrap文件
{% extends 'baseForm.html'%} {% block styles %} <link rel="stylesheet" href="{{ url_for('static',filename='style.css')}}"> {% endblock %} {% block content %} <h1 class='display-4'>Bootstrap Style Form</h1> <form method="post"> {{ form.csrf_token }} <div class="form-group"> {{ form.username.label }} {{form.username(class='form-control',required='')}} </div> <div class="form-group"> {{ form.password.label }} {{ form.password(class='form-control') }} </div> <div class="form-check"> {{ form.remember(class='form-check-input') }} {{ form.remember.label }} </div> {{ form.submit(class='btn btn-primary') }} </form> {% endblock content %}
8.4Template下的baseform文件——该文件内容不全所以看不到效果
<!DOCTYPE html> <html lang="en"> <head> {% block head %} {% block metas %} <meta charset="utf-8"> {% endblock metas%} <title> {% block title %} Form = HelloFlask {% endblock title %} </title> <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='favi')}}" {% block style %} <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename ='s')}}" {% endblock styles %} {% endblock head %} </head> <body> <nav> {% block nav %} <u1> <li><a href="{{ url_for('basic') }}">Home </a></li> </u1> {% endblock %} </nav> <main> {% for message in get_flashed_messages() %} <div class="alert"> {{ message }} </div> {% endfor %} {% block content %}{% endblock %} </main> <footer> {% block footer %} {% endblock %} </footer> {% block scripts %}{% endblock %} </body> </html>
8.5主文件下的form.py文件内容
# encoding=utf-8 from flask_wtf import FlaskForm from wtforms import Form, StringField, PasswordField, BooleanField, SubmitField from wtforms.validators import DataRequired, Length class LoginForm(FlaskForm): username = StringField("validate_Username", render_kw={'placeholder':'Your Username'}) password = PasswordField("Password", validators=[DataRequired(),Length(8,128)]) remember = BooleanField("Remember me") submit = SubmitField("Log in")
8.6Static下的style.css文件
body{ margin:auto; width:750px; } img{ max-width:710px; } nav u1{ list-style-type:none; margin:(); padding:(); overflow:hidden; background-color:#333; nav li{ float:left; } nav li.a{ display:block; color:white; text-align:center; padding:14px 16px; text-decoration:none; }
8.7在static文件夹下新建一个style.css的文件
body{ margin:auto; width:750px; } img{ max-width:710px; } nav u1{ list-style-type:none; margin:(); padding:(); overflow:hidden; background-color:#333; nav li{ float:left; } nav li.a{ display:block; color:white; text-align:center; padding:14px 16px; text-decoration:none; }
8.8最后的提交表单


浙公网安备 33010602011771号