form.validate_on_submit(): 和 {{ form.csrf_token }} 无法在具有两种形式的 view.html 中工作

form.validate_on_submit(): and {{ form.csrf_token }} not working in a view.html with two forms

http://127.0.0.1:5000/中:

如果默认值为“0”,则每个字段中的消息:“此字段为必填项”。 “compute0”和“compute1”上方有消息“csrf_token CSRF Token”。 只有 compute0 有效。

点击 compute0 显示结果。 单击 compute1 什么也没显示。

form 和 form1 有 3 个输入。 compute0 和 compute1 调用 (math.sin(r))* t + u.

from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import TextField, BooleanField, PasswordField, TextAreaField, validators
from compute import compute
from model import InputForm, InputForm1
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'Thisisasecret'
    
    @app.route('/', methods=['GET', 'POST']) 
    
    def index():
        form = InputForm(request.form)
        form1 = InputForm1(request.form)
        if form.validate_on_submit():
            if request.method == 'POST': 
                        r = form.r.data
                        t = form.t.data
                        u = form.u.data
                        s = compute(r,t,u)
            else:
                        s = None
    
            return render_template("view.html", form=form, form1=form1, s=s)
    
        
    
        if form1.validate_on_submit():
            if request.method == 'POST':
                    
                        r1 = form1.r1.data
                        t1 = form1.t1.data
                        u1 = form1.u1.data
                        s1 = compute(r1,t1,u1)
            else:
                        s1 = None
    
            return render_template("view.html", form=form, form1=form1, s1=s1)
    
        return render_template("view.html",form=form, form1=form1)
    
    if __name__ == '__main__':
        app.run(debug=True)

from flask_wtf import FlaskForm
from wtforms import validators, StringField, PasswordField,FloatField, validators

class InputForm(FlaskForm):
    r = FloatField(label='var_r',default=0,validators=[validators.DataRequired()])
    t = FloatField(label='var_t',default=0,validators=[validators.DataRequired()])
    u = FloatField(label='var_u',default=0,validators=[validators.DataRequired()])


class InputForm1(FlaskForm):
    r1 = FloatField(label='var_r1',default=0,validators=[validators.DataRequired()])
    t1 = FloatField(label='var_t1',default=0,validators=[validators.DataRequired()])
    u1 = FloatField(label='var_u1',default=0,validators=[validators.DataRequired()])

import math

def compute(r,t,u):
    return (math.sin(r))* t + u

<form method="post" action="">
{{ form.csrf_token }}
  {% for field in form %}
  
    <dt>{{ field.name }}
    <dd>{{ field|safe }} {{field.label }}

    
    {% if field.errors %}
      <ul class=errors>
      {% for error in field.errors %}
        <li>{{ error }}</li>
      {% endfor %}</ul>
    {% endif %}</dd>
  {% endfor %}

<p><input type=submit value=Compute0></form></p>
<h5> Valor: </h5>
<p>
{% if s != None %}
{{ s }}
{% endif %}
</p>

<form method="post" action="">
{{ form.csrf_token }}
  {% for field in form1 %}
  
    <dt>{{ field.name }}
    <dd>{{ field|safe }} {{field.label }}
    {% if field.errors %}
      <ul class=errors>
      {% for error in field.errors %}
        <li>{{ error }}</li>
      {% endfor %}</ul>
    {% endif %}</dd>
  {% endfor %}
 
<p><input type=submit value=Compute1></form></p>
<h5> Valor: </h5>
<p>
{% if s1 != None %}
{{ s1 }}
{% endif %}
</p>

如果我理解你的描述,你在同一页面上有 2 个独立的表格 两者POST 请求发送到 相同的视图函数 进行处理。

您想根据已提交的表单更新每个表单下方的 placeholder,我认为您需要执行 ajax 调用,因为(基于在上面的代码中)无法保留或保留其他形式的数据(例如数据库)

这样做:

  • 更新表格代码InputForm

由于 2 个表单具有相同的字段,您只需要一个用于此用例,然后调整和扩展具有不同字段集的其他表单的逻辑

from flask_wtf import FlaskForm
from wtforms.fields import FloatField, SubmitField
from wtforms.validators import DataRequired


class InputForm(FlaskForm):
    r = FloatField('r', validators=[DataRequired()])
    t = FloatField('t', validators=[DataRequired()])
    u = FloatField('u', validators=[DataRequired()])
    submit = SubmitField('Submit')
  • 更新模板
<!-- the first form -->
<form class="form" id="form-1" method="POST" novalidate>
  {{ form_1.csrf_token }}
  <div class="row">
    <div class="col">
      <div class="form-group row">
        <div class="col-sm-3">
          {{ form_1.r.label(class="col-form-label") }} <span class="text-danger">*</span>
        </div>
        <div class="col-sm-9">
          {{ form_1.r(class="form-control rounded-0 shadow-none", **{"placeholder":"0"}) }}
          <div class="invalid-feedback"></div>
        </div>
      </div>
    </div>
    <div class="col">
      <div class="form-group row">
        <div class="col-sm-3">
          {{ form_1.t.label(class="col-form-label") }} <span class="text-danger">*</span>
        </div>
       <div class="col-sm-9">
         {{ form_1.t(class="form-control rounded-0 shadow-none", **{"placeholder":"0"}) }}
         <div class="invalid-feedback"></div>
       </div>
      </div>
    </div>
    <div class="col">
      <div class="form-group row">
        <div class="col-sm-3">
          {{ form_1.u.label(class="col-form-label") }} <span class="text-danger">*</span>
        </div>
       <div class="col-sm-9">
         {{ form_1.u(class="form-control rounded-0 shadow-none", **{"placeholder":"0"}) }}
         <div class="invalid-feedback"></div>
       </div>
      </div>
    </div>
  </div>
  <div class="form-group row d-flex justify-content-between border bg-light p-3">
    {{ form_1.submit(class="btn btn-sm btn-primary rounded-0 shadow-none") }}
    <span class="placeholder">sin(r) * t + u = 0.00</span>
  </div>
</form>

<!-- the second form -->
<form class="form" id="form-2" method="POST" novalidate>
  {{ form_2.csrf_token }}
  <div class="row">
    <div class="col">
      <div class="form-group row">
        <div class="col-sm-3">
          {{ form_2.r.label(class="col-form-label") }} <span class="text-danger">*</span>
        </div>
        <div class="col-sm-9">
          {{ form_2.r(class="form-control rounded-0 shadow-none", **{"placeholder":"0"}) }}
          <div class="invalid-feedback"></div>
        </div>
      </div>
    </div>
    <div class="col">
      <div class="form-group row">
        <div class="col-sm-3">
          {{ form_2.t.label(class="col-form-label") }} <span class="text-danger">*</span>
        </div>
       <div class="col-sm-9">
         {{ form_2.t(class="form-control rounded-0 shadow-none", **{"placeholder":"0"}) }}
         <div class="invalid-feedback"></div>
       </div>
      </div>
    </div>
    <div class="col">
      <div class="form-group row">
        <div class="col-sm-3">
          {{ form_2.u.label(class="col-form-label") }} <span class="text-danger">*</span>
        </div>
       <div class="col-sm-9">
         {{ form_2.u(class="form-control rounded-0 shadow-none", **{"placeholder":"0"}) }}
         <div class="invalid-feedback"></div>
       </div>
      </div>
    </div>
  </div>
  <div class="form-group row d-flex justify-content-between border bg-light p-3">
    {{ form_2.submit(class="btn btn-sm btn-primary rounded-0 shadow-none") }}
    <span class="placeholder">sin(r) * t + u = 0.00</span>
  </div>
</form>

在你的视图函数中(这里我只是隔离了 Blueprint @bp 中的逻辑):

from flask import Blueprint, render_template, request, jsonify

from .forms import InputForm
from .utils import compute


bp = Blueprint(
    'home', 
    __name__, 
    static_folder='static',
    static_url_path='/home/static',
    template_folder='templates',
    # url_prefix=None,
    # subdomain=None,
    # url_defaults=None,
    # root_path=None,
    # cli_group=<object object>
)


@bp.route('/', methods=['GET', 'POST']) 
def index():

    form_1 = InputForm()
    form_2 = InputForm()

    if request.method == 'POST':

        # we handle the "POST" request of the submitted "form_1"
        if form_1.validate_on_submit():
            r = form_1.r.data
            t = form_1.t.data
            u = form_1.u.data
            s = compute(r,t,u)
            return jsonify(s=s if s else 0, errors={})
        else:
            return jsonify(s=0, errors=form_1.errors)
        
        # we handle the "POST" request of the submitted "form_2"
        if form_2.validate_on_submit():
            r = form_2.r.data
            t = form_2.t.data
            u = form_2.u.data
            s = compute(r,t,u)
            return jsonify(s=s if s else 0, errors={})
        else:
            return jsonify(errors=form_2.errors)

    return render_template("home/index.html", form_1=form_1, form_2=form_2)


  • ajax部分:

我建议你 如何利用 jinja2 模板继承来插入 locally a js (与css) 特定页面的块。

基于,我假设你正在使用 jquery 并且因为我们希望 jinja2 呈现此 javascript 行 url: "{{ url_for('home.index') }}", 所以在你的 template 中添加此代码

$(document).ready(function() {
  "use strict";

  $('.form').submit(function(e) {
    e.preventDefault();

    // get the id of the form currently submitted
    let id = $(this).attr('id');

    $.ajax({
      url: "{{ url_for('home.index') }}",
      type: 'POST',
      cache: false,
      dataType: 'json',
      data: $('#' + id).serialize(),  // serialize the form
      success: function(data) {

        // console.log(data);


        // sanitize the form before processing the submitted fields
        if( ! data.errors.hasOwnProperty('r')){
          $('#' + id + ' input[name=r]').removeClass('is-invalid').next(".invalid-feedback").text('');
        }
        
        if( ! data.errors.hasOwnProperty('t')){
          $('#' + id + ' input[name=t]').removeClass('is-invalid').next(".invalid-feedback").text('');
        }
        
        if( ! data.errors.hasOwnProperty('u')){
          $('#' + id + ' input[name=u]').removeClass('is-invalid').next(".invalid-feedback").text('');
        }


        // @see 
        if( ! jQuery.isEmptyObject(data.errors)) { // Errors (data.errors = {})

            console.log('Errors');
          
            if(data.errors.hasOwnProperty('r')){
              $('#' + id + ' input[name=r]').addClass('is-invalid').next(".invalid-feedback").text(data.errors['r']);
            }

            if(data.errors.hasOwnProperty('t')){
              $('#' + id + ' input[name=t]').addClass('is-invalid').next(".invalid-feedback").text(data.errors['t']);
            }

            if(data.errors.hasOwnProperty('u')){
              $('#' + id + ' input[name=u]').addClass('is-invalid').next(".invalid-feedback").text(data.errors['u']);
            }

          // update placeholder with the returned value
          $('#' + id + ' .placeholder').html('sin(r) * t + u = ' + data.s);


          } else { // Passed

          console.log('Passed');

          // get the submitted fields
          let r = $('#' + id + ' input[name=r]').val();
          let t = $('#' + id + ' input[name=t]').val();
          let u = $('#' + id + ' input[name=u]').val();

          // update placeholder with the returned value
          $('#' + id + ' .placeholder').html('sin(' + r + ') * ' + t + ' + ' + u + ' = ' + data.s);

          // reset fields
          $('#' + id + ' input[name=r]').val('');
          $('#' + id + ' input[name=t]').val('');
          $('#' + id + ' input[name=u]').val('');
        }
        
      }
    });
  });

});

最后这个

希望这能解决您的问题。