I have an array of forms I want rendered in a flask blueprint called fans. I am using sqlalchemy to SQLLite during dev to persist the data and flask-wtforms to render. The issue appears to be with DecimalRangeField – if I have two or more fans and change the slider on just one, the other slider appears to move to match it, despite the value of the DecimalRangeField being unchanged.
Here is the routes.py code with the “fix” of the redirect added:
@bp.route('/', methods=['GET', 'POST'])
def fans_index():
fans = Fan.query.all()
if fans.__len__() == 0:
return redirect(url_for('fans.newfan'))
form = FanForm(request.form)
if form.validate_on_submit(): # request.method == 'POST'
for fan in fans:
if fan.name == form.name.data:
fan.swtch = form.swtch.data
fan.speed = round(form.speed.data)
db.session.commit()
return redirect(url_for('fans.fans_index')) # <-- THIS is required, why?
else: # request.method == 'GET'
pass
forms = []
for fan in fans:
form = FanForm()
form.name.data = fan.name
form.swtch.data = fan.swtch
form.speed.data = fan.speed
forms.append(form)
return render_template('fans_index.html', title='Fans!', forms=forms)
Here is the form used:
class FanForm(FlaskForm):
name = HiddenField('Name')
swtch = BooleanField('Switch', render_kw={'class': 'swtch'})
speed = DecimalRangeField('Speed', render_kw={'class': 'speed'}, validators=[DataRequired()])
submit = SubmitField('Save Fan')
And here is the html template:
<h1>Fans</h1>
<div class="container">
<div class="row">
{% for form in forms %}
<div class="col mx-1 shadow-5-strong border border-white rounded" style="max-width: 220px">
<h2 class="ms-1">{{ form.name.data }}:</h2>
<form class="mx-auto ms-3" name="{{ form.name.data }}" action="" method="post">
{{ form.hidden_tag() }}
<div>{{ form.name }}</div>
<p>
{{ form.speed.label }}: <span class="speed_display_val">{{ form.speed.data | round }}%</span><br>
{{ form.speed(min=20) }}<br>
{% for error in form.speed.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.swtch.label }} <span class="ms-3">{{ form.swtch }}</span><br>
{% for error in form.swtch.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit }}</p>
</form>
</div>
{% endfor %}
</div>
</div>
I also have some simple javascript for this page to animate the slider and do submits when a user moves the slider or clicks a checkbox for turning the fans on and off:
/* script to animate the slider value changing and post data on slider mouseup or switch click */
const values = Array.from(document.getElementsByClassName('speed_display_val'));
const speeds = Array.from(document.getElementsByClassName('speed'));
const swtches = Array.from(document.getElementsByClassName('swtch')); //Note: switch is a reserved word in JS
speeds.forEach((speed, i) => {
speed.oninput = (e) => { values[i].textContent = Math.round(e.target.value) + '%' };
speed.onmouseup = () => { speed.form.requestSubmit() };
});
swtches.forEach((swtch) => {
swtch.onclick = () => { swtch.form.requestSubmit() };
});