除了在單個表單上實現多個提交按鈕,有時還須要在單個頁面上建立多個表單。好比,在程序的主頁上同時添加登陸和註冊表單。當在同一個頁面上添加多個表單時,咱們須要解決的問題是在視圖函數中判斷當前被提交的是哪一個表單。html
建立兩個表單,並在模板中分別渲染比較容易,但當提交某個表單是,就會遇到問題,Flask-WTF根據請求方法判斷表單是否提交,但並非判斷是哪一個表單被提交,因此咱們須要手動判斷,咱們知道被單擊的提交字段最終的data屬性值是布爾值,即True或False。而解析後的表單數據使用input字段的name屬性值做爲鍵匹配字段數據,就是說,若是兩個表單的提交字段名稱都是submit,那麼沒法判斷是哪一個表單的提交字段被單擊。session
解決問題的第一步就是爲兩個表單的提交字段設置不一樣的名稱,示例程序中的兩個表單以下所示:app
forms.py:爲兩個表單設置不一樣的提交字段名稱函數
from wtforms.validators import Email class SigninForm(FlaskForm): username = StringField('Username',validators=[DataRequired(),Length(1,20)]) password = PasswordField('Password', validators=[DataRequired(),Length(8,128)]) submit1 = SubmitField('Sign in') class RegisterForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1,20)]) email = StringField('Email', validators=[DataRequired(), Email(), Length(1,254)]) password = PasswordField('Password', validators=[DataRequired(), Length(8,128)]) submit2 = SubmitField('Register')
在視圖函數中,咱們分別實例化這兩個表單,根據提交字段的值來區分被提交的表單,以下所示:post
app.py:ui
from forms import SigninForm, RegisterForm @app.route('/multi-form', methods=['GET', 'POST']) def multi_form(): signin_form = SigninForm() register_form = RegisterForm() #validate()逐個對字段調用字段實例化時定義的驗證器,返回表示驗證結果的布爾值 if signin_form.submit1.data and signin_form.validate(): username = signin_form.username.data flash('%s, you just submit the Signin Form.' % username) return redirect(url_for('index')) if register_form.submit2.data and register_form.validate(): username = register_form.username.data flash('%s, you just submit the Register Form.' % username) return redirect(url_for('index')) return render_template('2form.html', signin_form = signin_form,register_form = register_form)
在視圖函數中,咱們爲兩個表單添加了各自的if判斷,在if語句的內如,分別執行各自的邏輯,以Signinform的if判斷爲例,若是signin_form.submit1.data的值是True,那麼說明用戶提交了登陸表單,這時手動調用signin_form.validate()對錶單進行驗證。url
這兩個表單類實例經過不一樣的變量名稱傳入模板,以便在模板中渲染對應的表單字段,以下所示:spa
2form.html:3d
{% extends 'base.html' %} {% from 'macros.html' import form_field %} {% block content %} <h1>Multiple Form in One Page with One View</h1> <h3>Login Form</h3> <form method = 'post'> {{ signin_form.csrf_token }} {{ form_field(signin_form.username) }} {{ form_field(signin_form.password) }} {{ signin_form.submit1 }} </form> <h3>Register Form</h3> <form method="post"> {{ register_form.csrf_token }} {{ form_field(register_form.username) }} {{ form_field(register_form.email) }} {{ form_field(register_form.password) }} {{ register_form.submit2 }} </form>
{% endblock %}
訪問127.0.0.1:5000/multi-form頁面,提交某個表單後,會在重定向後的頁面的提示消息中看到提交表單的名稱。code
除了經過提交按鈕判斷,更簡潔的方法是經過分離表單的渲染和驗證明現。這時表單的提交字段可使用同一個名稱,在視圖函數中處理表單時也只須要使用咱們熟悉的form.validate_on_submit()方法。
咱們在同一個視圖函數內處理兩類公國:渲染包含表單的模板(GET請求)、處理表單請求(POST請求)。若是想解耦這部分功能,也能夠分離成兩個視圖函數處理。當處理多個表單時,咱們能夠把表單的渲染在單獨的視圖函數中處理,以下所示:
@rouge('/multi-form-multi-view') def multi_form_multi_view(): signin_form = SigninForm() register_form = RegisterForm() return render_template('2form2view.html', signin_form=signin_form, register_form=register_form)
這個視圖只負責Get請求,實例化兩個表單類並渲染模板。另外再爲每個表單單首創建一個視圖函數來處理驗證工做。處理表單提交請求的視圖僅監聽POST請求。以下所示:
app.py:使用單獨的視圖函數處理表單提交的POST請求
@app.route('/handle-signin', methods=['POST']) # 僅傳入POST到methods中 def handle_signin(): signin_form = SigninForm() register_form = RegisterForm() if signin_form.validate_on_submit(): username = signin_form.username.data flash('%s , yoou just submit the Signin Form.' % username) return redirect(url_for('index')) return render_template('2form2view.html', signin_form = signin_form, register_form = register_form) @app.route('/handle-register', methods=['POST']) def handle_register(): signin_form = SigninForm() register_form = RegisterForm() if register_form.validate_on_submit(): username = register_form.username.data flash('%s, you just submit the Register Form.' % username) return redirect(url_for('index')) return render_template('2form2view.html', signin_form = signin_form, register_form = register_form)
在模板中,表單提交請求的目標URL經過action屬性設置,爲了讓表單提交時將請求發送到對應的URL,咱們須要設置action屬性,以下所示:
2form2view.html:
{% extends 'base.html' %} {% from 'macros.html' import form_field %} {% block content %} <h2>Multiple Form in One Page with Multiple View</h2> <h3>Login Form</h3> <form meghod="post" action="{{ url_for('handle_signin') }}"> {{ signin_form.csrf_token }} {{ form_field(signin_form.username) }} {{ form_field(signin_form.password) }} {{ signin_form.submit }} </form> <h3>Register Form</h3> <form method="post" action="{{ url_for('handle_register') }}"> {{ register_form.csrf_token }} {{ form_field(register_form.username) }} {{ form_field(register_form.email) }} {{ form_field(register_form.password) }} {{ register_form.submit }} </form> {% endblock %}
例中index視圖:
@app.route('/index') def index(): return render_template('index.html') index.html: {% extends 'base.html' %} {% block content %} <h1>Forms</h1> <ul> <li><a href="{{ url_for('basic') }}">Basic Form</a></li> <li><a href="{{ url_for('upload') }}">File Upload</a></li> <li><a href="{{ url_for('multi_upload') }}">Multiple Files Upload</a></li> <li><a href="{{ url_for('two_submits') }}">Multiple Submit Buttons</a></li> <li><a href="{{ url_for('multi_form') }}">Multiple Form</a></li> <li><a href="{{ url_for('multi_form_multi_view') }}">Multiple form Multiple view</a></li> </ul> {% endblock %}
訪問127.0.0.1:5000/2form2view.html,填入必填內容,點擊提交,在重定向後的頁面上提示提交表單的名稱
雖然如今能夠正常工做,可是有一個缺點,若是驗證未經過,須要將錯誤消息的form.errors字典傳入模板中。如:
{% for message in register_form.username.errors %} <small class="error">{{ message }}</small><br>
在處理表單的視圖中傳入表單錯誤信息,就意味着須要再次渲染模板,可是若是視圖函數中還涉及大量要傳入模板的變量操做,那麼這種方式會帶來大量的重複。
對於這個問題,通常的解決方式是經過其餘方式傳遞錯誤消息,而後統一重定向到渲染表單頁面的視圖。好比使用flash()函數迭代form.errors字典發送錯誤消息的函數:
def flash_errors(form):
for field, errors in form.errors.items():
for error in errors:
flash(u"Error in the %s field - %s" % (getattr(form, field).label.text, error))
若是你但願像往常同樣在表單字段下渲染錯誤消息,能夠直接將錯誤消息字段form.errors存儲到session中,而後重定向到用來渲染表單的multi_form_multi_view視圖。在模板中渲染表單字段錯誤時添加一個額外的判斷,從session獲取並遍歷錯誤消息。
在本例中,錯誤是在宏裏面渲染的:
{% macro form_field(field) %} {{ field.label }}<br> {{ field(**kwargs) }}<br> {% if field.errors %} {% for error in field.errors %} <small class="error">{{ error }}</small><br> {% endfor %} {% endif %} {% endmacro %}