使用 Flask 和 WTForms 在单个页面中创建多个表单 [英] Multiple forms in a single page using flask and WTForms

查看:30
本文介绍了使用 Flask 和 WTForms 在单个页面中创建多个表单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在同一页面上有多个表单将发布请求发送到同一个处理程序在烧瓶中.

I have multiple form on the same page that send post request to same handler in flask.

我正在使用 wtforms 生成表单.

I am generating forms using wtforms.

确定提交哪个表单的最佳方法是什么?

what is the best way to identify which form is submitted ?

我目前正在使用 action="?form=oneform".我认为应该有一些更好的方法达到同样的目的?

I am currently using action="?form=oneform". I think there should be some better method to achieve the same?

推荐答案

上面的解决方案有一个验证错误,当一个表单导致验证错误时,两个表单都显示错误消息.我改变了if的顺序来解决这个问题.

The solution above have a validation bug, when one form cause a validation error, both forms display an error message. I change the order of if to solve this problem.

首先,定义多个具有不同名称的SubmitField,如下所示:

First, define your multiple SubmitField with different names, like this:

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

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

....

然后在view.py中添加一个过滤器:

Then add a filter in 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 
....

现在问题解决了.

如果您想深入了解,请继续阅读.

If you want to dive into it, then continue read.

这是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).

When you call form.validate_on_submit(), it check if form is submitted by the HTTP method no matter which submit button was clicked. So the little trick above is just add a filter (to check if submit has data, i.e., form1.submit1.data).

另外,我们改变了if的顺序,所以当我们点击一​​个submit时,它只会调用validate()到这个表单,防止两种形式的验证错误.

Besides, we change the order of if, so when we click one submit, it only call validate() to this form, preventing the validation error for both form.

故事还没有结束.这是.data:

The story isn't over yet. Here is .data:

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

它返回一个带有字段名称(键)和字段数据(值)的字典,但是,我们的两个表单提交按钮具有相同的名称submit(key)!

It return a dict with field name(key) and field data(value), however, our two form submit button has same name submit(key)!

当我们点击第一个提交按钮(在 form1 中)时,来自 form1.submit1.data 的调用返回一个这样的字典:

When we click the first submit button(in form1), the call from form1.submit1.data return a dict like this:

temp = {'submit': True}

毫无疑问,当我们调用if form1.submit.data:时,它返回True.

There is no doubt when we call if form1.submit.data:, it return True.

当我们点击第二个提交按钮(在form2中)时,调用.dataif form1.submit.data:在dict中添加一个键值first,然后从 if form2.submit.data: 调用添加另一个键值,最后,dict 将是这样的:

When we click the second submit button(in form2), the call to .data in if form1.submit.data: add a key-value in dict first, then the call from if form2.submit.data: add another key-value, in the end, the dict will like this:

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

现在我们调用if form1.submit.data:,它返回True,即使我们点击的提交按钮在form2中.

Now we call if form1.submit.data:, it return True, even if the submit button we clicked was in form2.

这就是为什么我们需要用不同的名称定义这两个SubmitField.顺便说一句,感谢阅读(到这里)!

That's why we need to define this two SubmitField with different names. By the way, thanks for reading(to here)!

还有另一种方法可以在一个页面上处理多个表单.您可以使用多个视图来处理表单.例如:

There is another way to handle multiple forms on one page. You can use multiple views to handle forms. For example:

...
@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 属性设置为目标视图:

In the template (index.html), you need to render both forms and set the action attribute to 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>

这篇关于使用 Flask 和 WTForms 在单个页面中创建多个表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆