在单个页面中使用烧瓶和WTForms的多种forms
我有多个窗体发送到相同的处理程序在同一页面发送请求。
我使用wtforms生成表单。
什么是确定提交表格的最佳方式?
我目前正在使用action="?form=oneform"
。 我觉得应该有一些更好的方法来实现相同的?
我一直在使用两个瓶子片段的组合。 第一个为表单添加一个前缀 ,然后用validate_on_submit()检查前缀。 我还使用LouisRoché的模板来确定在表单中按下哪些button 。
引用Dan Jacob:
例:
form1 = FormA(prefix="form1") form2 = FormB(prefix="form2") form3 = FormC(prefix="form3")
然后,添加一个隐藏的字段(或只是检查一个提交字段):
if form1.validate_on_submit() and form1.submit.data:
引用LouisRoché的话:
我有我的模板:
<input type="submit" name="btn" value="Save"> <input type="submit" name="btn" value="Cancel">
并找出哪个button通过服务器端我在我的views.py文件中:
if request.form['btn'] == 'Save': something0 else: something1
一个简单的方法是为不同的提交字段使用不同的名称。 举个例子:
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.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" ...
上面的解决scheme有一个validation错误 ,当一个表单导致validation错误时,两个表单都显示错误消息。 我改变了if
解决这个问题的顺序。
首先,使用不同的名称定义多个SubmitField
,如下所示:
class Form1(Form): name = StringField('name') submit1 = SubmitField('submit') class Form2(Form): name = StringField('name') submit2 = SubmitField('submit') ....
然后在view.py
添加一个filter:
.... 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(self): """ Checks if form has been submitted. The default case is if the HTTP method is **PUT** or **POST**. """ return request and request.method in ("PUT", "POST")
当您调用form.validate_on_submit()
,无论点击哪个提交button,它都会检查表单是否由HTTP方法提交。 所以上面的小技巧只是添加一个filter(检查提交是否有数据,即form1.submit1.data
)。
此外,我们改变if
的顺序,所以当我们点击一个提交时, 它只会调用validate()
到这个表单 ,防止两个表单的validation错误。
这个故事还没有结束。 这里是.data
:
@property def data(self): return dict((name, f.data) for name, f in iteritems(self._fields))
它返回一个与字段名称(键)和字段数据(值)的字典,但是, 我们的两个表单提交button具有相同的名称submit
(键)!
当我们点击第一个提交button(在form1中)时,来自form1.submit1.data
的调用返回一个像这样的字典:
temp = {'submit': True}
毫无疑问,当我们调用if form1.submit.data:
,它会返回True
。
当我们点击第二个提交button(在form2中)时,调用.data
if form1.submit.data:
首先在dict中添加一个键值,然后从if form2.submit.data:
调用if form2.submit.data:
添加另一个键值最后,字典会这样:
temp = {'submit': False, 'submit': True}
现在我们调用if form1.submit.data:
它返回True
,即使我们点击的提交button是在form2中。
这就是为什么我们需要用不同的名字来定义这两个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') def register(): form = RegisterForm() if form.validate_on_submit(): ... ... @app.route('/login') def login(): form = LoginForm() if form.validate_on_submit(): ... ...
在模板(index.html)中,您需要呈现两个表单并将action
属性设置为target view:
<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>
作为其他答案,我也为每个提交button,为页面上的每个表单分配一个唯一的名称。
然后,flask web动作如下所示 – 请注意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"))