如何重写此 Flask 视图函数以遵循发布/重定向/获取模式



我有一个小的日志浏览器。它根据用户的输入检索并显示以前记录的记录列表。它不会更新任何内容。

代码非常简单,工作正常。这是一个简化版本:

@app.route('/log', methods=['GET', 'POST'])
def log():
    form = LogForm()
    if form.validate_on_submit():
        args = parse(form)
        return render_template('log.html', form=form, log=getlog(*args))
    return render_template('log.html', form=form)

但是它不遵循发布/重定向/获取模式,我想解决这个问题。

我应该在哪里存储发布的数据(即args)在发布和获取之间?标准或推荐的方法是什么?我应该设置饼干吗?我应该使用 flask.session 对象,在那里创建一个缓存吗?你能指出我正确的方向吗?大多数时候我在写后端...


更新:

我正在发布生成的代码。

@app.route('/log', methods=['POST'])
def log_post():
    form = LogForm()
    if form.validate_on_submit():
        session['logformdata'] = form.data
        return redirect(url_for('log'))
    # either flash errors here or display them in the template
    return render_template('log.html', form=form)
@app.route('/log', methods=['GET'])
def log():
    try:
        formdata = session.pop('logformdata')
    except KeyError:
        return render_template('log.html', form=LogForm())
    args = parse(formdata)
    log = getlog(args)
    return render_template('log.html', form=LogForm(data=formdata), log=log)

因此,最终,post/redirect/get 模式可以防止多次提交表单数据。由于您在此处POST实际上并未进行任何数据库更改,因此您使用的方法似乎很好。通常在模式中,POST对底层数据结构进行更改(例如更新/插入/删除),然后在重定向时查询更新的数据(SELECT),因此通常不需要在重定向和获取之间"存储"任何内容。

综上所述,我的方法是使用 Flask 会话对象,这是 Flask 为您管理的 cookie。你可以做这样的事情:

@app.route('/log', methods=['GET', 'POST'])
def log():
    form = LogForm()
    if form.validate_on_submit():
        args = parse(form)
        session['log'] = getlog(*args)
        return redirect(url_for('log'))
    saved = session.pop('log', None)
    return render_template('log.html', form=form, log=saved)

此外,要使用会话,您必须在应用程序配置中设置secret_key

烧瓶会话接口

更新 1/9/16

根据 ThiefMaster 的评论,此处重新排列了逻辑顺序,以允许对无效表单提交使用 WTForms 验证方法,因此无效表单提交不会丢失。

在 Flask 中执行 P/R/G 的常用方法是:

@app.route('/log', methods=('GET', 'POST'))
def log():
    form = LogForm()
    if form.validate_on_submit():
        # process the form data
        # you can flash() a message here or add something to the session
        return redirect(url_for('log'))
    # this code is reached when the form was not submitted or failed to validate
    # if you add something to the session in case of successful submission, try
    # popping it here and pass it to the template
    return render_template('log.html', form=form)

如果表单无法验证 WTForms 会保留在 POST 页面上,从而使用用户输入的数据预填充字段,并且可以在表单呈现期间显示每个字段的错误(通常人们会编写一些 Jinja 宏来轻松呈现 WTForm)

此方法维护表单数据以及重定向时的字段错误。如果您的表单有小数或日期,请使用 Flask-Session,因为它们会在使用常规 Flask 会话时搞砸

@app.route("/log", methods=["get", "post"])
def log():
    if request.method == "GET":
        form = session2form(LogForm) if "form" in session else LogForm()
        return render_template("log.html", form=form)
    else: # POST
        form = LogForm()
        if form.validate_on_submit():
            return redirect(url_for("...")) # your "success" endpoint
        # validation failed, so put form/errors in session and redirect to self:
        form2session(form)
        return redirect(url_for(request.endpoint))

两个辅助函数来保持干燥:

def session2form(form_cls):
    form_data, field_errors, form_errors = session["form"]
    form = form_cls(**form_data)
    form.form_errors = form_errors
    for field in form:
        field.errors = field_errors[field.name]
    session.pop("form", None)
    return form

def form2session(form):
    """can't store WTForm in session as it's not serializable,
    but can store form data and errors"""
    session["form"] = (
        form.data,
        {field.name: field.errors for field in form},
        form.form_errors,
    )

最新更新