Подтвердить что ты не робот

Несколько форм на одной странице с использованием колбы и WTForms

У меня есть несколько форм на той же странице, которые отправляют почтовый запрос тому же обработчику в колбе.

Я генерирую формы с использованием wtforms.

Каков наилучший способ определить, какая форма отправлена?

В настоящее время я использую action="?form=oneform". Я думаю, что должен быть какой-то лучший метод добиться того же?

4b9b3361

Ответ 1

Я использовал комбинацию из двух флеш-фрагментов. Первый добавляет префикс к форме, а затем вы проверяете префикс с validate_on_submit(). Я использую также шаблон Louis Roché, чтобы определить, какие кнопки вставляются в форму.

Процитировать Дэн Джейкоб:

Пример:

form1 = FormA(prefix="form1")
form2 = FormB(prefix="form2")
form3 = FormC(prefix="form3")

Затем добавьте скрытое поле (или просто проверьте поле отправки):

if form1.validate_on_submit() and form1.submit.data:

Процитировать Луи Роше:

У меня есть в моем шаблоне:

<input type="submit" name="btn" value="Save">
<input type="submit" name="btn" value="Cancel">

И чтобы выяснить, какая кнопка была передана на стороне сервера, у меня есть в файле views.py:

if request.form['btn'] == 'Save':
    something0
else:
    something1

Ответ 2

В приведенном выше решении есть ошибка проверки, когда одна форма вызывает ошибку проверки, обе формы отображают сообщение об ошибке. Я изменяю порядок if, чтобы решить эту проблему.

Сначала определите ваш кратный SubmitField с разными именами, например:

class Form1(Form):
    name = StringField('name')
    submit1 = SubmitField('submit')

class Form2(Form):
    name = StringField('name')
    submit2 = SubmitField('submit')

....

Затем добавьте фильтр в view.py:

....
form1 = Form1()
form2 = Form2()
....

if form1.submit1.data and form1.validate(): # notice the order 
....
if form2.submit2.data and form2.validate(): # notice the order 
....

Теперь проблема была решена.

Если вы хотите погрузиться в это, то продолжайте читать.

Вот validate_on_submit():

def validate_on_submit(self):
    """
    Checks if form has been submitted and if so runs validate. This is
    a shortcut, equivalent to ''form.is_submitted() and form.validate()''
    """
    return self.is_submitted() and self.validate()

А вот и is_submitted():

def is_submitted():
    """Consider the form submitted if there is an active request and
    the method is ''POST'', ''PUT'', ''PATCH'', or ''DELETE''.
    """
    return _is_submitted()  # bool(request) and request.method in SUBMIT_METHODS

Когда вы вызываете form.validate_on_submit(), он проверяет, отправлена ли форма методом HTTP, независимо от того, какая кнопка отправки была нажата. Итак, небольшая хитрость выше - это просто добавить фильтр (чтобы проверить, есть ли у данных данные, т.е. form1.submit1.data).

Кроме того, мы меняем порядок if, поэтому, когда мы нажимаем одну отправку, она вызывает только validate() этой формы, предотвращая ошибку проверки для обеих форм.

История еще не закончена. Вот .data:

@property
def data(self):
    return dict((name, f.data) for name, f in iteritems(self._fields))

Он возвращает слово с именем поля (ключ) и данными поля (значение), однако наша кнопка отправки двух форм имеет одинаковое имя submit (ключ)!

Когда мы нажимаем первую кнопку отправки (в форме 1), вызов из form1.submit1.data возвращает запрос, подобный следующему:

temp = {'submit': True}

Нет сомнений, что когда мы вызываем if form1.submit.data:, он возвращает True.

Когда мы нажимаем вторую кнопку отправки (в form2), вызов .data в if form1.submit.data: добавляет значение ключа в dict first, затем вызов из if form2.submit.data: добавляет другое значение ключа, в конце концов, диктат понравится так:

temp = {'submit': False, 'submit': True}

Теперь мы вызываем if form1.submit.data:, он возвращает True, даже если кнопка отправки, которую мы нажали, была в форме2.

Вот почему нам нужно определить эти два SubmitField с разными именами. Кстати, спасибо за чтение (здесь)!

Обновление

Существует еще один способ обработки нескольких форм на одной странице. Вы можете использовать несколько представлений для обработки форм. Например:

...
@app.route('/')
def index():
    register_form = RegisterForm()
    login_form = LoginForm()
    return render_template('index.html', register_form=register_form, login_form=login_form)

@app.route('/register', methods=['POST'])
def register():
    register_form = RegisterForm()
    login_form = LoginForm()

    if register_form.validate_on_submit():
        ...  # handle the register form
    # render the same template to pass the error message
    # or pass 'form.errors' with 'flash()' or 'session' then redirect to /
    return render_template('index.html', register_form=register_form, login_form=login_form)


@app.route('/login', methods=['POST'])
def login():
    register_form = RegisterForm()
    login_form = LoginForm()

    if login_form.validate_on_submit():
        ...  # handle the login form
    # render the same template to pass the error message
    # or pass 'form.errors' with 'flash()' or 'session' then redirect to /
    return render_template('index.html', register_form=register_form, login_form=login_form)

В шаблоне (index.html) необходимо отобразить обе формы и установить атрибут action для целевого представления:

<h1>Register</h1>
<form action="{{ url_for('register') }}" method="post">
    {{ register_form.username }}
    {{ register_form.password }}
    {{ register_form.email }}
</form>

<h1>Login</h1>
<form action="{{ url_for('login') }}" method="post">
    {{ login_form.username }}
    {{ login_form.password }}
</form>

Ответ 3

Простой способ - использовать разные имена для разных полей отправки. Для Пример:

forms.py:

class Login(Form):

    ...
    login = SubmitField('Login')


class Register(Form):

    ...
    register = SubmitField('Register')

views.py:

@main.route('/')
def index():

    login_form = Login()
    register_form = Register()


    if login_form.validate_on_submit() and login_form.login.data:
        print "Login form is submitted"

    elif register_form.validate_on_submit() and register_form.register.data:
        print "Register form is submitted"

    ...

Ответ 4

Как и другие ответы, я также назначаю уникальное имя для каждой кнопки отправки для каждой формы на странице.

Затем действие веб-флэшки выглядит следующим образом: обратите внимание на параметры formdata и obj, которые помогают создавать/сохранять поля формы соответственно:

@bp.route('/do-stuff', methods=['GET', 'POST'])
def do_stuff():
    result = None

    form_1 = None
    form_2 = None
    form_3 = None

    if "submit_1" in request.form:
        form_1 = Form1()
        result = do_1(form_1)
    elif "submit_2" in request.form:
        form_2 = Form2()
        result = do_2(form_2)
    elif "submit_3" in request.form:
        form_3 = Form3()
        result = do_3(form_3)

    if result is not None:
        return result

    # Pre-populate not submitted forms with default data.
    # For the submitted form, leave the fields as they were.

    if form_1 is None:
        form_1 = Form1(formdata=None, obj=...)
    if form_2 is None:
        form_2 = Form2(formdata=None, obj=...)
    if form_3 is None:
        form_3 = Form3(formdata=None, obj=...)

    return render_template("page.html", f1=form_1, f2=form_2, f3=form_3)


def do_1(form):
    if form.validate_on_submit():
        flash("Success 1")
        return redirect(url_for(".do-stuff"))


def do_1(form):
    if form.validate_on_submit():
        flash("Success 2")
        return redirect(url_for(".do-stuff"))

def do_3(form):
    if form.validate_on_submit():
        flash("Success 3")
        return redirect(url_for(".do-stuff"))

Ответ 5

Пример: несколько WTForm на одной HTML-странице

app.py

"""
Purpose Create multiple form on single html page.

Here we are having tow forms first is Employee_Info and CompanyDetails
"""
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, FloatField, validators
from wtforms.validators import InputRequired

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

class EmployeeInfo(FlaskForm):
    """
    EmployeeInfo class will have Name,Dept
    """
    fullName = StringField('Full Name',[validators.InputRequired()])
    dept = StringField('Department',[validators.InputRequired()])

class CompanyDetails(FlaskForm):
    """
    CompanyDetails will have yearOfExp. 
    """
    yearsOfExp = IntegerField('Year of Experiece',[validators.InputRequired()]) 


@app.route('/', methods = ['GET','POST'] )
def index():
    """
    View will render index.html page.
    If form is validated then showData.html will load the employee or company data.
    """
    companydetails = CompanyDetails()
    employeeInfo = EmployeeInfo()

    if companydetails.validate_on_submit():
        return render_template('showData.html', form = companydetails)

    if employeeInfo.validate_on_submit():
        return render_template('showData.html', form1 = employeeInfo)   

    return render_template('index.html',form1 = employeeInfo, form = companydetails)

if __name__ == '__main__':
    app.run(debug= True, port =8092)

шаблоны/index.html

<html>
    <head>
    </head>
    <body>  
        <h4> Company Details </h4>

        <form method="POST" action="{{url_for('index')}}">

            {{ form.csrf_token }}

            {{ form.yearsOfExp.label }} {{ form.yearsOfExp }}       

            <input type="submit" value="Submit">
        </form>

        <hr>
        <h4> Employee Form </h4>

        <form method="POST" action="{{url_for('index')}}" >

            {{ form1.csrf_token }}

            {{ form1.fullName.label }} {{ form1.fullName }}

            {{ form1.dept.label }} {{ form1.dept }}

            <input type="submit" value="Submit">
        </form>
    </body>
</html>

showData.html

<html>
    <head> 
    </head> 
    <body>
        {% if form1 %}
        <h2> Employee Details </h2>
            {{ form1.fullName.data }}
            {{ form1.dept.data }}
        {% endif %}
        {% if form %}
            <h2> Company Details </h2>
                {{ form.yearsOfExp.data }}      
        {% endif %}     
    </body>
</html>

Ответ 6

I haven't used WTForms but should work regardless. This is a very quick and simple answer; all you need to do is use different values for the submit button. You can then just do a different def based on each. 

in index.html: 

    <div>
        <form action="{{ url_for('do_stuff')}}" method="POST">
            <h1>Plus</h1>
            <input type = "number" id = "add_num1" name = "add_num1" required><label>Number 1</label><br>
            <input type = "number" id = "add_num2" name = "add_num2" required><label>Number 2</label><br>
            <input type = "submit" value = "submit_add" name = "submit" ><br>
        </form>
        <p>Answer: {{ add }}</p>
    </div>

    <div>
        <form action="{{ url_for('do_stuff')}}" method="POST">
            <h1>Minus</h1>
            <input type = "number" id = "min_num1" name = "min_num1" required><label>Number 1</label><br>
            <input type = "number" id = "min_num2" name = "min_num2" required><label>Number 2</label><br>
            <input type = "submit" value = "submit_min" name = "submit"><br>
        </form>
        <p>Answer: {{ minus }}</p>
    </div>

in app.py:

@app.route('/',methods=["POST"])
def do_stuff():
    if request.method == 'POST':
        add = ""
        minus = ""
        if request.form['submit'] == 'submit_add':
            num1 = request.form['add_num1']
            num2 = request.form['add_num2']
            add = int(num1) + int(num2)

        if request.form['submit'] == 'submit_min':
            num1 = request.form['min_num1']
            num2 = request.form['min_num2']
            minus = int(num1) - int(num2)
    return render_template('index.html', add = add, minus = minus)