您的位置:

FlaskForm详解

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>");
        }
    });