フラスコとWTFormsを使用して1つのページに複数のフォーム



Answers

上記の解決策には検証バグがあります.1つのフォームで検証エラーが発生すると、両方のフォームにエラーメッセージが表示されます。 私はこの問題を解決する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の順序を変更するので、submitをクリックするvalidate()が呼び出され、両方のフォームの検証エラーが防止されます。

物語はまだ終わっていない。 ここは.dataです:

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

フィールド名(キー)とフィールドデータ(値)を持つdictを返しますが、2つのフォーム送信ボタンは同じ名前のsubmit (key)を持っています!

最初の送信ボタン(form1)をクリックすると、 form1.submit1.dataからの呼び出しは次のようなdictを返します。

temp = {'submit': True}

if form1.submit.data:を呼び出すときは間違いありませんが、 True返しTrue

2番目の送信ボタン(form2)をクリックするとif form1.submit.data: .dataへの呼び出しが.dataif form1.submit.data: 最初にdictにキー値を追加し、次にif form2.submit.data:からの呼び出しに別のキー値を追加します、最終的に、dictはこれを好きになるでしょう:

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

ここでif form1.submit.data:を呼び出すとif form1.submit.data:私たちがクリックした送信ボタンがform2であったとしてもTrue返します。

そのため、この2つのSubmitFieldを別の名前で定義する必要があります。 ところで、読んでくれてありがとう(ここまで)!

更新

1つのページで複数のフォームを処理する別の方法があります。 複数のビュー使用してフォームを処理できます。 例えば:

...
@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>
Question

フラスコの同じハンドラに投稿要求を送る同じページに複数のフォームがあります。

私はwtformsを使用してフォームを生成しています。

どんなフォームが提出されたかを特定する最良の方法は何ですか?

私は現在action="?form=oneform"ます。 私は同じことを達成するためのより良い方法があるはずだと思いますか?




他の答えとして、ページ上の各フォームの送信ボタンごとに固有の名前を割り当てます。

次に、フラスコの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"))



Related