FlaskForm,是Flask框架中的一个表单处理扩展,它可以帮我们更方便地管理表单,实现表单验证、提交等功能,减轻了我们的工作压力。本文从多个方面对FlaskForm做详细的阐述,以便读者更好地理解与使用它。
一、FlaskForm二级联动
在我们的Web应用中,常常需要进行二级联动功能的实现,比如省、市、区的地址选择,或者是商品类型、子类的选择等等。使用FlaskForm可以很方便地实现这样的功能。
首先我们需要定义表单类,下面是一个例子:
from wtforms import SelectField, Form, validators
choices_a = [("", "请选择..."), ("1", "选项1"), ("2", "选项2"), ("3", "选项3"), ("4", "选项4")]
choices_b = [("", "请选择..."), ("a", "选项A"), ("b", "选项B"), ("c", "选项C"), ("d", "选项D")]
class TestForm(Form):
select_a = SelectField("级别1", choices=choices_a, validators=[validators.DataRequired()])
select_b = SelectField("级别2", choices=choices_b, validators=[validators.DataRequired()])
接下来,我们需要在视图函数中使用这个表单类。在GET请求中,我们需要传递一个空的表单实例到前端,让用户进行选择;在POST请求中,我们需要对用户选择的数据进行处理并返回结果。
from flask import render_template, request
@app.route("/double_select", methods=["GET", "POST"])
def double_select():
form = TestForm(request.form)
if request.method == "POST" and form.validate():
select_a = form.select_a.data
select_b = form.select_b.data
# 处理选项数据...
return "success"
return render_template("double_select.html", form=form)
在前端模板中,我们需要利用FlaskForm提供的表单插件来渲染二级联动的功能。下面是一个例子:
<form method="post" action="">
{{ form.select_a.label }} {{ form.select_a }}
{{ form.select_b.label }} {{ form.select_b }}
<input type="submit" value="提交">
</form>
{{ form.hidden_tag() }}
...
<script>
$(function() {
$("#select_a").on("change", function() {
$.get("/get_select_b", {"select_a": $(this).val()}, function(data) {
var optionsHtml = "";
$.each(data, function(key, value) {
optionsHtml += "<option value='" + key + "'>" + value + "</option>";
});
$("#select_b").html(optionsHtml);
}, "json");
});
});
</script>
在这个例子中,我们使用了flask_wtf插件来渲染表单,使用了jQuery库来实现异步加载子类选项数据的功能。
二、FlaskForm CSRF
在我们的Web应用中,跨站请求伪造(CSRF)是一种常见的安全漏洞,攻击者可以通过构造恶意请求来实现用户信息、资金等的窃取或者篡改。使用FlaskForm提供的CSRF保护可以很好地防御这种安全漏洞。
在Flask中,我们可以通过在FlaskForm实例中设置SECRET_KEY来启用CSRF保护。下面是一个例子:
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config["SECRET_KEY"] = "your-secret-key"
csrf = CSRFProtect(app)
这里我们设置了一个随机的SECRET_KEY,用来加密和验证TOKEN,保证应用的安全性。然后我们使用CSRFProtect包装我们的Flask应用来启用CSRF保护。
启用CSRF保护后,我们需要在前端模板中使用FlaskForm提供的表单插件来渲染form标签,其中包含了_csrf_token的隐藏字段。下面是一个例子:
<form method="post" action="">
{{ form.username.label }} {{ form.username }}
{{ form.password.label }} {{ form.password }}
<input type="submit" value="提交">
</form>
{{ form.hidden_tag() }}
当我们在POST请求中提交表单数据时,FlaskForm会自动从表单数据中获取_csrf_token字段,并且与其中包含的加密签名进行比较验证,如果验证失败则抛出异常。
三、FlaskForm联合查询
在我们的Web应用中,常常需要实现复杂的联合查询功能,比如商品名称、类型、价格的多条件组合查询等等。使用FlaskForm可以很方便地实现这样的功能。
FlaskForm提供了各种表单字段类型来方便我们实现多条件查询,比如StringField、SelectField、DateField等。下面是一个例子:
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, DateField
from wtforms.validators import DataRequired
class QueryForm(FlaskForm):
name = StringField("名称")
category = SelectField("分类", choices=[("all", "全部"), ("1", "分类1"), ("2", "分类2")])
price_min = StringField("最小价格")
price_max = StringField("最大价格")
create_time = DateField("创建日期", format="%Y-%m-%d")
update_time = DateField("更新日期", format="%Y-%m-%d")
在视图函数中,我们使用这个表单类来接收并且处理用户提交的查询条件。下面是一个例子:
@app.route("/query", methods=["GET", "POST"])
def query():
form = QueryForm(request.form)
if request.method == "POST" and form.validate():
name = form.name.data.strip() or None
category = form.category.data
if category == "all":
category = None
try:
price_min = float(form.price_min.data.strip())
except:
price_min = None
try:
price_max = float(form.price_max.data.strip())
except:
price_max = None
create_time = form.create_time.data or None
update_time = form.update_time.data or None
# 处理查询条件...
return "success"
return render_template("query.html", form=form)
在前端模板中,我们可以使用FlaskForm提供的表单插件来渲染表单。下面是一个例子:
<form method="post" action="">
{{ form.name.label }} {{ form.name }}
{{ form.category.label }} {{ form.category }}
{{ form.price_min.label }} {{ form.price_min }}
{{ form.price_max.label }} {{ form.price_max }}
{{ form.create_time.label }} {{ form.create_time }}
{{ form.update_time.label }} {{ form.update_time }}
<input type="submit" value="查询">
</form>
{{ form.hidden_tag() }}
四、FlaskForm CSRF取消
在某些情况下,我们可能需要在特定表单中取消FlaskForm的CSRF保护,比如在我们需要让用户在不登录的情况下进行某些操作的时候。FlaskForm提供了一个@csrf_exempt装饰器来完成这个功能。
在视图函数中,我们只需要在函数上方加上@csrf_exempt装饰器即可取消FlaskForm的CSRF保护。下面是一个例子:
from flask_wtf.csrf import csrf_exempt
@app.route("/some_action", methods=["POST"])
@csrf_exempt
def some_action():
# 处理不需要CSRF保护的操作...
return "success"
五、FlaskForm Python三级联动
除了常见的二级联动,有些特定的业务场景可能需要实现三级或者更多级联动的功能。FlaskForm同样可以很方便地实现这些功能。
在实现三级联动的时候,我们需要使用三个FlaskForm实例来分别表示三个下拉框。下面是一个例子:
from wtforms import Form, SelectField
class SelectAForm(Form):
select_a = SelectField("级别1")
class SelectBForm(Form):
select_b = SelectField("级别2")
class SelectCForm(Form):
select_c = SelectField("级别3")
然后我们需要在视图函数中使用这三个表单来实现三级联动。在GET请求中,我们需要传递一族空表单到前端;在POST请求中,我们需要处理用户的选择,并且动态返回下一级的选项。下面是一个例子:
@app.route('/three_levels')
def three_levels():
select_a_form = SelectAForm()
select_b_form = SelectBForm()
select_c_form = SelectCForm()
return render_template("three_levels.html", select_a_form=select_a_form, select_b_form=select_b_form, select_c_form=select_c_form)
@app.route("/get_b")
def get_b():
select_b_form = SelectBForm()
id = request.args.get("id", 0, type=int)
if id == 1:
select_b_form.select_b.choices = [("", "选择..."), (1, "选项1"), (2, "选项2"), (3, "选项3")]
elif id == 2:
select_b_form.select_b.choices = [("", "选择..."), (4, "选项4"), (5, "选项5"), (6, "选项6")]
elif id == 3:
select_b_form.select_b.choices = [("", "选择..."), (7, "选项7"), (8, "选项8"), (9, "选项9")]
return jsonify(select_b_form.select_b.choices)
@app.route("/get_c")
def get_c():
select_c_form = SelectCForm()
id = request.args.get("id", 0, type=int)
if id == 1:
select_c_form.select_c.choices = [("", "选择..."), (1, "选项1"), (2, "选项2"), (3, "选项3")]
elif id == 2:
select_c_form.select_c.choices = [("", "选择..."), (4, "选项4"), (5, "选项5"), (6, "选项6")]
elif id == 3:
select_c_form.select_c.choices = [("", "选择..."), (7, "选项7"), (8, "选项8"), (9, "选项9")]
return jsonify(select_c_form.select_c.choices)
在前端模板中,我们需要利用FlaskForm提供的表单插件来渲染三个下拉框。其中第一级会触发AJAX请求,返回第二级的选项;第二级会触发AJAX请求,返回第三级的选项。下面是一个例子:
<form>
{{ select_a_form.select_a.label }} {{ select_a_form.select_a }}
{{ select_b_form.select_b.label }} {{ select_b_form.select_b }}
{{ select_c_form.select_c.label }} {{ select_c_form.select_c }}
</form>
<script>
$(function() {
$("#select_a").on("change", function() {
var id = $(this).val();
if (id) {
$.get("/get_b", {"id": id}, function(data) {
var optionsHtml = "<option value=''>-</option>";
$.each(data, function(key, value) {
optionsHtml += "<option value='" + key + "'>" + value + "</option>";
});
$("#select_b").html(optionsHtml).change();
}, "json");
} else {
$("#select_b").html("<option value=''>-</option>");
$("#select_c").html("<option value=''>-</option>");
}
});