У меня есть массив форм, которые я хочу отобразить в виде колбы, называемой вентиляторами. Я использую sqlalchemy для SQLLite во время разработки, чтобы сохранить данные и flask-wtforms для рендеринга. Похоже, проблема связана с DecimalRangeField: если у меня есть два или более вентилятора и я меняю ползунок только на одном, другой ползунок перемещается в соответствии с ним, несмотря на то, что значение данных DecimalRangeField остается неизменным. Примечание. Код ниже работает, проблема возникает, когда удаляется строка перенаправления, которую я выделил ниже.
Вот код Routes.py с добавленным «исправлением» перенаправления:
@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)
Вот используемая форма:
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')
А вот html-шаблон:
<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>
У меня также есть простой javascript для этой страницы, который анимирует слайдер и отправляет сообщения, когда пользователь перемещает ползунок или устанавливает флажок для включения и выключения вентиляторов:
/* 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() };
});
FlaskForm
автоматически будет использовать значения из flask.request.form
и flask.request.files
. Чтобы обойти эту проблему, вы можете передать None
в качестве атрибута formdata
формы. Таким образом, перенаправление, которое сбрасывает flask.request.form
, больше не требуется.
Тогда ваш код будет выглядеть примерно так.
@bp.route('/', methods=['GET', 'POST'])
def fans_index():
if Fan.query.count() == 0:
return redirect(url_for('.newfan'))
form = FanForm(request.form)
if form.validate_on_submit():
if fan := Fan.query.filter_by(name=form.name.data).first():
fan.swtch = form.swtch.data
fan.speed = round(form.speed.data)
db.session.commit()
forms = [FanForm(formdata=None, obj=fan) for fan in Fan.query.all()]
return render_template('fans_index.html', **locals())
Отличный. Вы объяснили, почему это не работает, и не только предоставили рабочий код, но и несколько других примеров более питонических способов сделать то же самое, что я пытался сделать. Как человек с многолетним опытом программирования, но новичок в Python, я ценю это.