Я пытаюсь использовать две формы Flask на одной странице. Первая форма собирает информацию для вывода списка доступных баз данных, которые передаются в качестве выбора в поле «SelectField» второй формы.
В моем view.py я определяю свои формы
class AvailableBDDForm(FlaskForm):
host = StringField('host',
validators=\[DataRequired()\])
port = StringField('port',
validators=\[DataRequired()\])
user = StringField('user',
validators=\[DataRequired()\])
password = StringField('password', validators=\[DataRequired()\])
submitAvailableBDD = SubmitField('METTRE A JOUR LA LISTE')
next = HiddenField()
class ControlerBDDForm(FlaskForm):
database = SelectField('database', validators=\[DataRequired()\])
submit = SubmitField('SE CONNECTER')
next = HiddenField()
def update_choices(self, databases):
self.database.choices = databases
А также мой маршрут
@app.route("/analye-bdd/", methods=("GET","POST",))
def analyse_bdd():
availableForm = AvailableBDDForm()
controlerForm = ControlerBDDForm()
selected_options = []
scroll = ""
bdd = BDD()
available_databases = bdd.lister_bdd()
try:
if availableForm.validate_on_submit():
print("first if")
bdd.update_parameters(availableForm.host.data, availableForm.port.data, availableForm.user.data, availableForm.password.data)
available_databases = bdd.lister_bdd()
print(available_databases)
controlerForm.update_choices(available_databases)
if controlerForm.validate_on_submit():
print("helloooo")
except Exception as e:
print(f"An error occurred: {e}")
return render_template(
"analyse-bdd.html",
title = "Analyze",
AvailableBDDForm=availableForm,
ControlerBDDForm=controlerForm,
selected_options=selected_options,
scroll_to=scroll,
host = bdd.host,
port=bdd.port,
database=bdd.database,
user=bdd.user,
password=bdd.password
)
И вот что я поместил на свою HTML-страницу:
<form method = "POST" action = "{{ url_for('analyse_bdd')}}">
<input type = "hidden" name = "csrf_token" value = "{{ csrf_token() }}">
{{ AvailableBDDForm.host(size=30, value=host, class = "texteInput")}}
{{ AvailableBDDForm.port(size=10, value=port) }}
{{ AvailableBDDForm.user(size=30, value=user) }}
{{ AvailableBDDForm.password(size=30, value=password) }}
{{ AvailableBDDForm.next }}
<br>
{{ AvailableBDDForm.submitAvailableBDD(class_ = "btn btn-primary") }}
</form>
<form method = "POST" action = "{{ url_for('analyse_bdd')}}">
<input type = "hidden" name = "csrf_token" value = "{{ csrf_token() }}">
<label>Database</label>
{{ ControlerBDDForm.database }}
{{ ControlerBDDForm.submit(class_ = "btn btn-primary") }}
</form>
Когда я нажимаю первую кнопку отправки (связанную с AvailableBDDForm), терминал показывает:
first if ['db1', 'db2']
Так что это работает. Увы, когда я нажимаю вторую кнопку отправки (связанную с ControlerBDDForm):
An error occurred: Choices cannot be None.
Я не получаю ни одного отпечатка (сначала если или здравствуйте), но получаю эту ошибку.
Я действительно не понимаю, что происходит, плюс логика внутри попытки вся перепутана, как я и раньше пробовал
@app.route("/analye-bdd/", methods=("GET","POST",))
def analyse_bdd():
availableForm = AvailableBDDForm()
controlerForm = ControlerBDDForm()
selected_options = []
scroll = ""
bdd = BDD()
available_databases = bdd.lister_bdd()
try:
availableFormBool = availableForm.validate_on_submit()
controlerFormBool = controlerForm.validate_on_submit()
print(availableFormBool, controlerFormBool)
if availableForm.validate_on_submit():
print("first if")
bdd.update_parameters(availableForm.host.data, availableForm.port.data, availableForm.user.data, availableForm.password.data)
available_databases = bdd.lister_bdd()
print(available_databases)
controlerForm.update_choices(available_databases)
if controlerForm.validate_on_submit():
print("helloooo")
except Exception as e:
print(f"An error occurred: {e}")
return render_template(
"analyse-bdd.html",
title = "Analyze",
AvailableBDDForm=availableForm,
ControlerBDDForm=controlerForm,
selected_options=selected_options,
scroll_to=scroll,
host = bdd.host,
port=bdd.port,
database=bdd.database,
user=bdd.user,
password=bdd.password
)
Чтобы увидеть, какая форма была отправлена при нажатии, но я просто ничего не получил на терминале, ни результат
print(availableFormBool, controlerFormBool)
или отпечаток "first if"
, ничего...
Кроме того, аналогичный пост существует в stackoverflow (колба нескольких форм на одной странице), но мои формы уже различаются по идентификатору, имени и значению, поэтому это не решает мою проблему.
заранее спасибо
Обновлено: Исходная проблема решена, но у меня есть еще один вопрос.
На моей HTML-странице есть список флажков (ввод), и я могу получить доступ к их содержимому, на мой взгляд, с помощью следующего кода:
`for field_name in bdd.analyze_options:
if request.form.get(field_name):
selected_options.append(request.form.get(field_name))
print("selected options", selected_options)
analyze_results = bdd.affichage_analyse(selected_options)
print(f"analyze results {analyze_results}")``
Я разместил это здесь:
@app.route("/analyze/", methods=("GET","POST",))
def analyze():
bdd = BDD()
form = AvailableBDDForm(request.form, data=session.get('avail_bdd', {}))
selected_options = []
analyze_results = {}
scroll_to = ""
if form.validate_on_submit():
data = {k:v for k,v in form.data.items() if k in ('host', 'port', 'user', 'password',)}
if session.get('avail_bdd', {}) != data:
session['avail_bdd'] = data
return redirect(url_for('analyze'))
if 'avail_bdd' in session:
bdd.update_parameters(**session['avail_bdd'])
form1 = ControlerBDDForm(request.form, data=session.get('ctrl_bdd', {}))
form1.update_choices(bdd.lister_bdd())
if form1.validate_on_submit():
session['ctrl_bdd'] = {k:v for k,v in form1.data.items() if k in ('database',)}
print(form1.database.data)
scroll_to = "for-scroll-results"
for field_name in bdd.analyze_options:
if request.form.get(field_name):
selected_options.append(request.form.get(field_name))
print("selected options", selected_options)
analyze_results = bdd.affichage_analyse(selected_options)
print(f"analyze results {analyze_results}")
return redirect(url_for('analyze'))
return render_template('analyze.html', **locals(),
title = "Analyze BDD")
Я не знаю, почему, selected_options имеет контент только тогда, когда я нажимаю кнопку отправки в AvailableBDDForm. Но мне бы хотелось, чтобы эта часть кода, касающаяся флажков, была только тогда, когда проверен ControlerBDDForm... Я пробовал разные места и альтернативы этому коду, но безрезультатно.
В моем примере сеанс используется для временного сохранения записей формы.
Если действительные данные изменились при отправке первой формы по сравнению с данными, сохраненными в сеансе, они обновляются в сеансе. Если это не так, данные сессии используются для заполнения второй формы. После отправки второй формы ее записи можно вывести.
Чтобы запросить флажки, вы можете добавить SelectMultipleField
в соответствующую форму, в которой настраиваются виджеты. Параметры добавляются так же, как и при использовании SelectField
. Однако из-за ошибки данные полей необходимо задавать вручную, чтобы они сохранили свой статус.
from flask import session
from wtforms.fields import SelectMultipleField
from wtforms.widgets import ListWidget, CheckboxInput
# ...
class AvailableBDDForm(FlaskForm):
host = StringField('Host',
validators=[DataRequired()])
port = StringField('Port',
validators=[DataRequired()])
user = StringField('User',
validators=[DataRequired()])
password = StringField('Password',
validators=[DataRequired()])
submit = SubmitField('METTRE A JOUR LA LISTE')
next = HiddenField()
class ControlerBDDForm(FlaskForm):
database = SelectField('Database', validators=[DataRequired()])
options = SelectMultipleField('Analyze Options',
option_widget=CheckboxInput(),
widget=ListWidget(prefix_label=False))
submit = SubmitField('SE CONNECTER')
next = HiddenField()
def update_choices(self, databases):
self.database.choices = databases
def update_options(self, options, data=[]):
self.options.choices = options
if not self.is_submitted():
self.options.data = data
@app.route('/analyze', methods=['GET', 'POST'])
def analyze():
bdd = BDD()
form = AvailableBDDForm(request.form, data=session.get('avail_bdd', {}))
if form.validate_on_submit():
data = {k:v for k,v in form.data.items() if k in ('host', 'port', 'user', 'password',)}
if session.get('avail_bdd', {}) != data:
session['avail_bdd'] = data
return redirect(url_for('analyze'))
if 'avail_bdd' in session:
bdd.update_parameters(**session['avail_bdd'])
data = session.get('ctrl_bdd', {})
form1 = ControlerBDDForm(request.form, data=data)
form1.update_choices(bdd.lister_bdd())
form1.update_options(bdd.analyze_options, data.get('options', []))
if form1.validate_on_submit():
session['ctrl_bdd'] = {k:v for k,v in form1.data.items() if k in ('database','options',)}
print(form1.database.data)
print(form1.options.data)
return redirect(url_for('analyze'))
return render_template('analyze.html', **locals())
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<meta name = "viewport" content = "width=device-width, initial-scale=1">
<title>Analyze</title>
</head>
<body>
<form method = "POST">
{{ form.hidden_tag() }}
<div>
{{ form.host.label() }}
{{ form.host() }}
</div>
<div>
{{ form.port.label() }}
{{ form.port() }}
</div>
<div>
{{ form.user.label() }}
{{ form.user() }}
</div>
<div>
{{ form.password.label() }}
{{ form.password() }}
</div>
{{ form.submit() }}
</form>
{% if form1 -%}
<form method = "POST">
{{ form1.hidden_tag() }}
<div>
{{ form1.database.label() }}
{{ form1.database() }}
</div>
<div>
{{ form1.options.label() }}
{{ form1.options() }}
</div>
{{ form1.submit() }}
</form>
{% endif -%}
</body>
</html>
Если вы хотите использовать более двух форм, я рекомендую использовать параметр URL, который увеличивается в зависимости от отправленной формы.
@app.route('/analyze', methods=['GET', 'POST'])
def analyze():
bdd = BDD()
form = AvailableBDDForm(request.form, data=session.get('avail_bdd', {}))
step = request.args.get('step', 1, type=int)
match step:
case 1:
if form.validate_on_submit():
data = {k:v for k,v in form.data.items() if k in ('host', 'port', 'user', 'password',)}
if session.get('avail_bdd', {}) != data:
session['avail_bdd'] = data
return redirect(url_for('analyze', step=step+1))
case 2:
if 'avail_bdd' in session:
bdd.update_parameters(**session['avail_bdd'])
data = session.get('ctrl_bdd', {})
form1 = ControlerBDDForm(request.form, data=data)
form1.update_choices(bdd.lister_bdd())
form1.update_options(bdd.analyze_options, data.get('options', []))
if form1.validate_on_submit():
session['ctrl_bdd'] = {k:v for k,v in form1.data.items() if k in ('database',)}
print(form1.database.data)
print(form1.options.data)
# possibly here forward
# return redirect(url_for('analyze_1', step=step+1))
# ...
case _:
pass
return render_template('analyze.html', **locals())
<form method = "POST" action = "{{ url_for('analyze', step=1) }}">
{# ... #}
</form>
{% if form1 -%}
<form method = "POST" action = "{{ url_for('analyze', step=2) }}">
{# ... #}
</form>
{% endif -%}
@GoodMorningStarshine Я добавил флажки в свой пример. Получайте удовольствие от своего проекта.
Спасибо большое, это сработало! Однако сейчас у меня есть другая проблема, но мне здесь недостаточно места, я добавлю остальную часть комментария в конце моего исходного сообщения.