在单个页面中使用烧瓶和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动作如下所示 – 请注意formdataobj参数,这些参数帮助相应地初始化/保留表单字段:

 @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"))