表单相关[使用方法]
在flask学习笔记(三)请求与响应一节中提到了表单与视图函数数据交互的过程。在MVC架构中[其实flask不能算严格意义上的MVC,这里暂且将其视作MVC] 我们已经详细介绍了Controller,即视图函数;View,即模板。但我们发现了一个问题,模板与视图函数的交互是双向的:模板渲染视图函数传递的数据;把请求中获取的数据交给视图函数处理,为了更好的学习后者,我们需要学习表单相关内容。
简单的HTML表单&后端交互 index.html
1 2 3 4 5 6 7 <form method="post" action="http://localhost:5000/index"> <label for="username">用户名</label><br> <input type="text" name="username" placeholder="用户名"><br> <label for="password">密码</label><br> <input type="password" name="password" placeholder="密码"><br> <input type="submit" name="submit" value="登录"> </form>
primary-form.py
1 2 3 4 5 @app.route('/index', methods=['POST', 'GET']) def index(): print(request.form.get('username')) print(request.form.get('password')) return '登录成功'
直接编写html不仅需要花费学习成本,且会增加代码量和降低代码可读性,所以引入了一些插件库来辅助编写表单。WTForms app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from flask import Flask, render_template, request from wtforms import Form, StringField, PasswordField from wtforms import validators app = Flask(__name__, template_folder="templates") class LoginForm(Form): username = StringField(label="用户名", validators=[validators.DataRequired(message="用户名不能为空"), ],) password = PasswordField(label="密码", validators=[validators.DataRequired(message="密码不能为空"), ],) @app.route('/login', methods=["GET", "POST"]) def login(): if request.method == "GET": form = LoginForm() return render_template("login.html", form=form) else: form = LoginForm(formdata=request.form) if form.validate(): if form.username.data == 'lockcy' and form.password.data == 'lockcy': return render_template('success.html', username=form.username.data) return render_template('fail.html', username=form.username.data) else: print(form.errors, "错误信息") return render_template("login.html", form=form) if __name__ == '__main__': app.run(debug=True)
login.html
1 2 3 4 5 <form method="post"> <p>{{form.username.label}} {{form.username}} {{form.username.errors[0] }}</p> <p>{{form.password.label}} {{form.password}} {{form.password.errors[0] }}</p> <input type="submit" value="提交"> </form>
常用的WTForms字段
字段类
说明
对应的HTML标识
BooleanField
复选框,值会被处理为True或False
<input type=”checkbox”>
DateField
文本字段,值会被处理为datetime.date对象
<input type=”text”>
DateTimeField
文本字段,值会被处理为datetime.datetime对象
<input type=”text”>
StringField
文本字段
<input type=”text”>
FileField
文件上传字段
<input type=”file”>
FloatField
浮点数字段,值会被处理为浮点型
<input type=”text”>
PasswordField
密码文本字段
<input type=”password”>
StringField
文本字段
<input type=”text”>
IntegerField
整数字段
<input type=”text”>
RadioField
一组单选框
<input type=”radio”>
SelectField
下拉列表
<select><option></option></select>
SelectMutipleField
多选下拉列表
<select multiple><option></option></select>
SubmitField
表单提交按钮
<input type=”submit”>
TextAreaField
多行文本字段
<textarea></textarea>
HiddenField
隐藏文本字段
<input type=”hidden”>
常用的WTForms验证器
验证器
说明
DataRequired(message=None)
验证数据是否有效
Email(message=None)
验证Email地址
EqualTo(fieldname, message=None)
验证两个字段值是否相同
InputRequired(message=None)
验证是否有数据
IPAddress(ipv4=True, ipv6=False, message=None)
验证IP地址是否合法,python2需要ipaddress包支持
Length(min=- 1, max=- 1, message=None)
验证输入的长度是否在给定范围之内
MacAddress(message=None)
验证mac地址是否合法
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)
确保输入值不在可选值列表中
Flask-WTF Flask-WTF 提供了简单地 WTForms 的集成。 app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, validators from flask import Flask, render_template, request app = Flask(__name__, template_folder="templates") app.secret_key = 'test' # flaskform使用csrf_token,必须设置secret_key app.debug = True class LoginForm(FlaskForm): username = StringField(label="用户名", validators=[validators.DataRequired(message="用户名不能为空"), ],) password = PasswordField(label="密码", validators=[validators.DataRequired(message="密码不能为空"), ],) @app.route('/login', methods=["GET", "POST"]) def login(): if request.method == "GET": form = LoginForm() return render_template("login.html", form=form) else: form = LoginForm(formdata=request.form) if form.validate(): if form.username.data == 'lockcy' and form.password.data == 'lockcy': return render_template('success.html', username=form.username.data) return render_template('fail.html', username=form.username.data) else: print(form.errors, "错误信息") return render_template("login.html", form=form) if __name__ == '__main__': app.run(debug=True)
login.html
1 2 3 4 5 6 <form method="post"> <p>{{form.csrf_token }}</p> <p>{{form.username.label}} {{form.username}} {{form.username.errors[0] }}</p> <p>{{form.password.label}} {{form.password}} {{form.password.errors[0] }}</p> <input type="submit" value="提交"> </form>
是不是感觉和WTForms几乎一样,但仔细看还是能发现不同。 即使我们没有手动设置csrf_token,flaskform在表单中也为我们设置了防止跨站请求的token,csrf_token需要secret_key为其签名,因此需要设置secret_key。再查看html源码
1 2 3 <p><input id="csrf_token" name="csrf_token" type="hidden" value="ImIwMjA1MWU5OTZhNzU4MWZiYzNiOWQ5ODg2ZDNkNjIwZmQ2MTY3ZmYi.YEcsbA.Gcgdx7CHkKZKW4ti13M699HIbD0"></p> <p><label for="username">用户名</label> <input id="username" name="username" required type="text" value=""> </p> <p><label for="password">密码</label> <input id="password" name="password" required type="password" value=""> </p>
其他配置 禁用 CSRF 保护: form = Form(csrf_enabled=False) 全局禁用 CSRF 保护: WTF_CSRF_ENABLED = False 单独密钥: WTF_CSRF_SECRET_KEY = ‘test’
app.config[‘WTF_CSRF_ENABLED’] = False app.config[‘WTF_CSRF_ENABLED’] = False
文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from flask import Flask, render_template, flash from werkzeug.utils import secure_filename from flask_wtf.file import FileField, FileRequired, FileAllowed from flask_wtf import FlaskForm from wtforms import SubmitField app = Flask(__name__) app.debug = True app.secret_key = 'test' app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 class UploadForm(FlaskForm): """用户上传文件的表单""" file = FileField(label="图片", validators=[FileRequired(), FileAllowed(['jpg', 'png'], '只能上传图片')]) submit = SubmitField() @app.route('/upload/', methods=('GET', 'POST')) def upload(): form = UploadForm() if form.validate_on_submit(): filename = secure_filename(form.file.data.filename) form.file.data.save('uploads/' + filename) flash('文件上传成功') else: filename = None flash('文件上传失败') return render_template('upload.html', form=form, filename=filename) if __name__ == '__main__': app.run()
upload.html
1 2 3 4 5 6 7 8 9 {% for message in get_flashed_messages() %} <div class="alert">{{ message }}</div> {% endfor %} {% block content %}{% endblock %} <form method="post" enctype="multipart/form-data"> {{ form.csrf_token }} {{ form.file }}<br> {{ form.submit }}<br> </form>
Flask-CKEditor富文本编辑器 引入CKEditor资源 为了使用CKEditor,我们首先要在模板中引入CKEditor的JavaScript等资源文件。推荐的做法是自己编写资源引用语句,你可以在CKEditor提供的Online Builder构建一个自定义的资源包,下载解压后放到项目的static目录下, 并引入资源包内的ckeditor.js文件,比如(实际路径按需调整):
1 <script src="{{ url_for('static', filename='ckeditor/ckeditor.js') }}"></script>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from flask_ckeditor import CKEditor, CKEditorField from flask import Flask, render_template, request from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, validators app = Flask(__name__) app.debug = True app.secret_key = 'test' ckeditor = CKEditor(app) class RichTextForm(FlaskForm): title = StringField('标题', validators=[validators.DataRequired()]) body = CKEditorField('正文', validators=[validators.DataRequired()]) submit = SubmitField('发布') @app.route('/ckeditor', methods=["GET", "POST"]) def ckeditor(): form = RichTextForm() if request.method == 'GET': return render_template('ckeditor.html', form=form) else: print(form.body.data) return render_template('ckeditor.html', form=form) if __name__ == '__main__': app.run()
ckeditor.html
1 2 3 4 5 {{ form.csrf_token }} {{ form.title.label }} {{ form.title() }} {{ form.body.label }} {{ form.body() }} {{ form.submit }} {{ ckeditor.config(name='body') }}
效果如下
WTForms文档:https://wtforms.readthedocs.io/en/2.3.x/ Flask-wtf文档:http://www.pythondoc.com/flask-wtf/ flask-ckeditor使用: http://greyli.com/flask-ckeditor-integrate-rich-text-editor/