flask 單個頁面多個表單(單視圖處理、多視圖處理) --


單個頁面多個表單

除了在單個表單上實現多個提交按鈕,有時還需要在單個頁面上創建多個表單。比如,在程序的主頁上同時添加登錄和注冊表單。當在同一個頁面上添加多個表單時,我們需要解決的問題是在視圖函數中判斷當前被提交的是哪個表單。

單視圖處理

創建兩個表單,並在模板中分別渲染比較容易,但當提交某個表單是,就會遇到問題,Flask-WTF根據請求方法判斷表單是否提交,但並不是判斷是哪個表單被提交,所以我們需要手動判斷,我們知道被單擊的提交字段最終的data屬性值是布爾值,即True或False。而解析后的表單數據使用input字段的name屬性值作為鍵匹配字段數據,就是說,如果兩個表單的提交字段名稱都是submit,那么無法判斷是哪個表單的提交字段被單擊。

解決問題的第一步就是為兩個表單的提交字段設置不同的名稱,示例程序中的兩個表單如下所示:

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')

 

在視圖函數中,我們分別實例化這兩個表單,根據提交字段的值來區分被提交的表單,如下所示:

app.py:

 

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()對表單進行驗證。

這兩個表單類實例通過不同的變量名稱傳入模板,以便在模板中渲染對應的表單字段,如下所示:

2form.html:

 

{% 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頁面,提交某個表單后,會在重定向后的頁面的提示消息中看到提交表單的名稱。

 

 

 

 

 

 

多視圖處理

除了通過提交按鈕判斷,更簡潔的方法是通過分離表單的渲染和驗證實現。這時表單的提交字段可以使用同一個名稱,在視圖函數中處理表單時也只需要使用我們熟悉的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 %}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM