Как использовать несколько форм на одной странице с Python/Flask?

Я пытаюсь использовать две формы 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... Я пробовал разные места и альтернативы этому коду, но безрезультатно.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В моем примере сеанс используется для временного сохранения записей формы.

Если действительные данные изменились при отправке первой формы по сравнению с данными, сохраненными в сеансе, они обновляются в сеансе. Если это не так, данные сессии используются для заполнения второй формы. После отправки второй формы ее записи можно вывести.

Чтобы запросить флажки, вы можете добавить 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 06.06.2024 11:34

@GoodMorningStarshine Я добавил флажки в свой пример. Получайте удовольствие от своего проекта.

Detlef 06.06.2024 16:23

Другие вопросы по теме