desmond
desmond

Reputation: 2081

Flask Form data duplicates on submit

i am trying to populate a table of current values, then change it with intention of finding the diffs of original and after. i simplify my code in the following to replicate the issue: -

webapp.py

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, DecimalField, fields
import pandas as pd

app=Flask(__name__)
app.config['SECRET_KEY'] = 'wtf'

class stockForm(FlaskForm):
    stock=StringField()
    price= DecimalField()

    def __init__(self, csrf_enabled=False, *args, **kwargs):
        super(stockForm, self).__init__(csrf_enabled=csrf_enabled, *args, **kwargs)

class stockListForm(FlaskForm):
    stockItem=fields.FieldList(fields.FormField(stockForm))


@app.route('/sEntry', methods=['GET','POST'])
def sEntry():
    form=stockListForm()
    stocklist=pd.DataFrame(data=[['abc',10.17],['bcd',11.53],['edf',12.19]],columns=['stock','price'])    

    for stock in stocklist.itertuples():
        sForm=stockForm()
        sForm.stock=stock.stock
        sForm.price=stock.price
        form.stockItem.append_entry(sForm)

    if form.validate_on_submit():
        results = []
        for idx, data in enumerate(form.stockItem.data):
            results.append(data)
        print(results)
        del form
        return render_template('results.html', results=results)
    print(form.errors)
    return render_template('sEntry.html',form=form)


if __name__=='__main__':
    app.run(debug=True, use_reloader=True, host='0.0.0.0', port=int('5050'))

sEntry.html

<html lang="en">
  <head>
    <meta charset="utf-8">
  </head>
<body>

    <form action="" method="POST" name="form">
    {{ form.name}}
    {{ form.hidden_tag() }}
    <div>
        <table>
            <thead >
            <tr class="col">
                <th style="width: 30px">stock</th>
                <th style="width: 50px">price</th>
            </tr>
            </thead>
            {% for stock in form.stockItem %}
            <tr class="col">
                <td>{{ stock.stock }}</td>
                <td>{{ stock.price }}</td>
            </tr>
            {% endfor %}
        </table>
    </div>
    <p><input type="submit" name="edit" value="Send"></p>
    </form>
</body>
</html>

results.html

<ul>
{% for line in results %}
    <li>{{ line }}</li>
{% endfor %}

</ul>

if I am to change the values of a few of the field, the variable results generated will have duplicate of 6 rows of data from my original 3 rows in the dataframes e.g.

{'price': Decimal('10.17'), 'stock': 'abc'}
{'price': Decimal('13'),    'stock': 'bcd'}
{'price': Decimal('12.19'), 'stock': 'edf'}
{'price': 10.17, 'stock': 'abc'}
{'price': 11.529999999999999, 'stock': 'bcd'}
{'price': 12.19, 'stock': 'edf'}

Furthermore, i also have issues my original Decimals used in turning into some long float values, in above example, i change bcd value from 11.53 to 13, the original value become long float figure, the rest that i didnt edit stay as original.

I could have the dirty solution of cutting the results into half and compare values between both halves, rounding those long floats to find values that have change, but seems very inefficient.

can anyone assist?

Upvotes: 4

Views: 1606

Answers (1)

plaes
plaes

Reputation: 32766

Firstly, you need to use proper Decimal type in the Pandas DataFrame. (Which can be handled by Pandas by using Numpy's dtype with an object).

Secondly, you were filling the form with original data when POST request occurred.

Somewhat fixed view function would look like this:

@app.route('/', methods=['GET','POST'])
def sEntry():
    # Create form and fill it with request data
    form = stockListForm(request.form)

    # Set up initial data with proper Decimal objects
    stocklist=pd.DataFrame(data=[['abc',Decimal('10.17')],['bcd',Decimal('11.53')],['edf',Decimal('12.19')]],columns=['stock','price'])    

    # Handle valid POST request
    if form.validate_on_submit():
        # Convert form data to dictionary (so we can later easily query stock price)
        stocks = {i['stock']: i['price'] for i in form.stockItem.data}

        # Generate result (as generator) ...
        results = ((i.stock, i.price, i.price - stocks[i.stock]) for i in stocklist.itertuples())

        # ... and push it to template
        return render_template('results.html', results=results)

    print(form.errors)

    # ...build initial form for GET request 
    for stock in stocklist.itertuples():
        sForm=stockForm()
        sForm.stock=stock.stock
        sForm.price=stock.price
        form.stockItem.append_entry(sForm)

    return render_template('sEntry.html',form=form)

Upvotes: 2

Related Questions