Несколько форм на одной странице с использованием колбы и WTForms
У меня есть несколько форм на той же странице, которые отправляют почтовый запрос тому же обработчику
в колбе.
Я генерирую формы с использованием wtforms.
Каков наилучший способ определить, какая форма отправлена?
В настоящее время я использую action="?form=oneform"
. Я думаю, что должен быть какой-то лучший метод
добиться того же?
Ответы
Ответ 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)