0

In http://127.0.0.1:5000/:

If defaults are "0", message in each field: "This field is required". There are the message "csrf_token CSRF Token" above "compute0" and "compute1". Only compute0 works.

click on compute0 shows the resulted. click on compute1 showns nothing.

form and form1 have 3 inputs. compute0 and compute1 call (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>
An 225
  • 3
  • 1

1 Answers1

0

if i understand your description, you have 2 independent forms on the same page that both send POST requests to the same view function to process.

you want to update the placeholder below of each form depending on which form is already submitted, here i think you need to perform an ajax call since (based on your code above) there's no way to keep or persist the data of the other form (database for e.g)

so to do:

  • update the code of the Form InputForm

since the 2 forms have the same fields you need just one for this use case and then adapt and extend the logic for other forms with different set of fields

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')
  • update the template
<!-- 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>

in your view function (here i just i isolated the logic in 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)


  • the ajax part:

i suggest you my answer on how to take advantage of jinja2 template inheritance to insert locally a js (the same with css) blocks for particular page.

base on, i assume your are using jquery and since we want jinja2 to render this javascript line url: "{{ url_for('home.index') }}", so in your template add this code

$(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 https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object
        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('');
        }
        
      }
    });
  });

});

and finally this a screenshot

hope this solves your issue.

cizario
  • 2,927
  • 3
  • 7
  • 22
  • yes, i make an assumption `jquery` is already loaded, maybe my answer on other topic could help you : https://stackoverflow.com/questions/62641283/how-to-use-python-flask-in-javascript-file-js/62643201#62643201 – cizario Jun 30 '20 at 17:16
  • Error message: jinja2.exceptions.UndefinedError: 'form' is undefined "i assume your are using jquery, so in your template add this code" I'm learning flask. I don't know anything about jquery yet. – An 225 Jun 30 '20 at 17:19
  • @An225 i updated my answer with code reviewed, it works for sure (it just used one blueprint) to isolate the logic. – cizario Jul 02 '20 at 13:48
  • your answers are challenging for me. Now I have to learn Blueprint. – An 225 Jul 04 '20 at 13:18
  • and i enjoy challenges, that's why i put much time on stackoverflow hunting good questions, reading from many sources and try developing a working apps. i would recommend you starting with the official flask tutorial and then **prettyprinted** youtube channel. – cizario Jul 04 '20 at 13:23