Flask WebForm
Date: Mar. 3rd, 2019
Lecturer: Chia
OUTLINE
- Web Form
- Flask-WTF
- Bootstrap 表單模板
- 轉址與使用者 session
- 閃現(flash) 訊息
- 用 Flask-Moment 來將日期與時間當地化
Web Form
- 使用者提供資料,讓伺服器接收與處理。
- 通常使用POST request。
- 安裝擴充套件:Flask-WTF
$ mkdir webform
$ cd webform
$ python -m venv venv
$ venv\Scripts\activate
(venv) $ pip install flask
開啟虛擬環境
hello.py初始化
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
(venv) $ set FLASK_APP=hello.py
(venv) $ set FLASK_DEBUG=1
(venv) $ flask run
執行Flask
安裝擴充套件:Flask-WTF
(venv) $ pip install flask-wtf
- Flask-WTF內含無關特定框架的WTForms套件。
- Flask-WTF不需要做app層級的初始化。
- 在app組態設置密鑰(secret key)
- 在app定義表單類別
- 轉譯表單HTML
- 在app的view函式中處理表單
Basic HTML Form
在app組態設置密鑰(secret key):
- 防止使用者session的內容被算改。
- 避免受到跨網站請求造照(CSRF,cross-site request forgery)的攻擊。
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
在app定義表單類別:
- 每一個Web表單都是用一個繼承FlaskForm的類別來表示。
- 類別內包含欄位、欄位物件、驗證函式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Web Form</title>
</head>
<body>
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
</body>
</html>
轉譯表單HTML:
在templates資料夾下,建立webform.html
- {{ form.hidden_tag() }} 定義一個隱藏的表單欄位,可用來實作CSRF防護
- {{ form.name(id='my-text-field') }} 可透過 id 定義CSS樣式
from flask import render_template
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('webform.html', form=form, name=name)
在app的view函式中處理表單:
- validate_on_submit()方法:
在表單被送出且所有欄位驗證函式都收到資料時回傳True。
LAB- 1
- 請新增欄位名稱為a的BooleanField(),必須有資料驗證,並於html上顯示其<label>和<input>
- 請新增欄位名稱為b的TextAreaField(),無資料驗證,並於html上顯示其<label>和<input>
- 請擇一給予特定的 id,並定義其CSS樣式
安裝擴充套件:Flask-Bootstrap
(venv) $ pip install flask-bootstrap
- 透過匯入Bootstrap的表單模板,來轉譯Flask-WTF表單。
- Bootstrap表單模板:bootstrap/wtf.html
- 在建立app實例時同時初始化
- 新增一個基礎模版(base_index.html)
- webform.html繼承基礎模板,並套用wtf.html
Bootstrap表單模板
在建立app實例時同時初始化:
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap(app)
新增一個基礎模版(base_index.html):
- 此基礎模版會繼承bootstrap/base.html
{% extends 'bootstrap/base.html' %}
{% block head %}
{{ super() }}
<title>{% block title %}{% endblock %} - My Application</title>
{% endblock %}
{% block content %}
<div class="container">
{% block page_content %}{% endblock %}
</div>
{% endblock %}
webform.html 繼承基礎模板,並套用wtf.html:
- wtf.quick_form() 接收Flask-WTF表單物件。
{% extends "base_index.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}webform{% endblock %}
{% block head %}
{{ super() }}
{% endblock %}
{% block page_content %}
<div class="page_header">
<h1>Hello,
{% if name %}
{{ name }}
{% else %}
Stranger
{% endif %}!</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
轉址與使用者session
- 原因:重新整理網頁時,瀏覽器會重複送出上一個送出的request,亦即重複送出表單資料。
- 解決方法:利用轉址(redirect) - Post/Redirect/Get 模式
轉址(redirect)
- 為一種特殊的回應,常用於處理web表單。
- 常見的四種回應:
- HTTP狀態碼
- 回應物件
- 轉址(redirect)
- 錯誤處理
# 轉址(redirect)
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
轉址與使用者session
然而,當Post request一結束,表單資料即消失。
因此,需要透過session(私用的存放區)在兩次request之間"記得"它。
- 使用轉址(Redirect)來回應POST請求,轉址的內容為URL。
- 當瀏覽器收到轉址回應時,會用GET發出轉址URL。
from flask import session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('webform.html', form=form, name=session.get('name'))
url_for(endpoint)
redirect(url_for('index')) 亦可寫成 redirect('/')
- 第一個且唯一的引數是endpoint(端點)。
- 預設下,路由的endpoint為view函式名稱。
- 呼叫 url_for('index', _external=True)
- 收到絕對URL,在本範例為 http://127.0.0.1:5000/
Request
用戶端發出request,伺服器會接收並呼叫view函式來處理。
URL map存有URL與view函式之間的對應關係。
(venv) $ python
>>> from hello import app
>>> app.url_map
LAB - 2
-
請在app中,定義名稱為web的view函式,路由為/web,將之轉址至任一網站。
閃現(flash)訊息
- 顯示更新訊息,可能用於確認、警告或錯誤。
from flask import flash
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like tou have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('webform.html', form=form, name=session.get('name'))
修改base_index.html基礎模板:
- 以轉譯app所定義的訊息。
- 基礎模板為轉譯閃現訊息的最佳地點。
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}
get_flashed_messages()
得到的訊息在下次呼叫此函數時就無法取得,故只會閃現一次。
將日期與時間當地化
安裝擴充套件:Flask-Moment
(venv) $ pip install flask-moment
- 讓瀏覽器讀取電腦的時區與地區,並用JavaScript轉換並轉譯成當地的時間。
- Moment.js:Flask擴充套件,需要jQuery.js。
初始化Flask-Moment
from flask_moment import Moment
moment = Moment(app)
base_index.html中,匯入Moment.js程式庫
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}
- 若使用Flask-Bootstrap,則只需加入Moment.js即可。
添加datetime變數
from datetime import datetime
@app.route('/')
def index():
return render_template('webform.html', current_time=datetime.utcnow())
使用時戳 (timestamp)
webform.html中,用Flask-Moment來轉譯時戳
{% block content %}
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
{{ super() }}
{% endblock %}
使用時戳 (timestamp)
- format('LLL') 根據電腦的時區與地區來轉譯日期與時間。
- 從 'L' 到 'LLLL' 代表四個等級的詳細程度。
- fromNow() 轉譯相對時戳。如:“a few seconds ago”
base_index.html中,更改時戳轉譯的語言
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.locale('es')}}
{% endblock %}
更改時戳轉譯的語言
-
轉為西班牙文'es'
LAB-3
-
請將時戳轉譯的語言改為德文
-
請將時戳的格式改為最精簡
Thanks for your listening
Flask - Web Form
By BessyHuang
Flask - Web Form
- 379