Flask开发系列之Web表单
Flask開發系列之Web表單
?
簡單示例
from flask import Flask, request, render_templateapp = Flask(__name__) @app.route('/', methods=['GET', 'POST']) def home():return render_template('home.html')@app.route('/signin', methods=['GET']) def signin_form():return render_template('form.html')@app.route('/signin', methods=['POST']) def signin():# receive the data from submitusername = request.form['username']password = request.form['password']if username == 'admin' and password == 'password':return render_template('signin-ok.html', username=username)return render_template('form.html', message='Bad username or password', username=username)if __name__ == '__main__':app.run()?
<html> <head><title>Please Sign In</title> </head> <body>{% if message %}<p style="color:red">{{ message }}</p>{% endif %}<form action="/signin" method="post"><p>Please sign in:</p><p><input name="username" placeholder="Username" value="{{ username }}"></p><p><input name="password" placeholder="Password" type="password" value="{{ password }}"></p><p><button type="submit">Sign In</button></p></form> </body> </html> form.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <h1>hello,{{ username }}</h1> </body> </html> signin-ok?
跨站請求偽造保護
默認情況下,Flask-WTF 能保護所有表單免受跨站請求偽造(Cross-Site Request Forgery,
CSRF)的攻擊。惡意網站把請求發送到被攻擊者已登錄的其他網站時就會引發 CSRF 攻擊。
?
為了實現 CSRF 保護,Flask-WTF 需要程序設置一個密鑰。Flask-WTF 使用這個密鑰生成
加密令牌,再用令牌驗證請求中表單數據的真偽。設置密鑰的方法如示例:
?
app.config 字典可用來存儲框架、擴展和程序本身的配置變量。使用標準的字典句法就能 把配置值添加到 app.config 對象中。這個對象還提供了一些方法,可以從文件或環境中導 入配置值。 SECRET_KEY 配置變量是通用密鑰,可在 Flask 和多個第三方擴展中使用。如其名所示,加 密的強度取決于變量值的機密程度。不同的程序要使用不同的密鑰,而且要保證其他人不 知道你所用的字符串。?
表單類
使用 Flask-WTF 時,每個 Web 表單都由一個繼承自 Form 的類表示。這個類定義表單中的
一組字段,每個字段都用對象表示。字段對象可附屬一個或多個驗證函數。驗證函數用來
驗證用戶提交的輸入值是否符合要求。
StringField 構造函數中的可選參數 validators 指定一個由驗證函數組成的列表,在接受
用戶提交的數據之前驗證數據。驗證函數 Required() 確保提交的字段不為空。
WTForms支持的HTML標準字段
字段類型 說 明 StringField 文本字段 TextAreaField 多行文本字段 PasswordField 密碼文本字段 HiddenField 隱藏文本字段 DateField 文本字段,值為 datetime.date 格式 DateTimeField 文本字段,值為 datetime.datetime 格式 IntegerField 文本字段,值為整數 DecimalField 文本字段,值為 decimal.Decimal FloatField 文本字段,值為浮點數 BooleanField 復選框,值為 True 和 False RadioField 一組單選框 SelectField 下拉列表 SelectMultipleField 下拉列表,可選擇多個值 FileField 文件上傳字段 SubmitField 表單提交按鈕 FormField 把表單作為字段嵌入另一個表單 FieldList 一組指定類型的字段WTForms驗證函數
驗證函數 說 明 Email 驗證電子郵件地址 EqualTo 比較兩個字段的值;常用于要求輸入兩次密碼進行確認的情況 IPAddress 驗證 IPv4 網絡地址 Length 驗證輸入字符串的長度 NumberRange 驗證輸入的值在數字范圍內 Optional 無輸入值時跳過其他驗證函數 Required 確保字段中有數據 Regexp 使用正則表達式驗證輸入值 URL 驗證 URL AnyOf 確保輸入值在可選值列表中 NoneOf 確保輸入值不在可選值列表中?
?
表單渲染
兩種方式:
-
flask-bootstrap渲染
-
一般渲染(常用)
flask-bootstrap渲染
flask-boostrap是bootstrap的flask擴張,它可以提供wtf.html文件中的form_field函數來進行渲染:
{% import "bootstrap/wtf.html" as wtf %} <form class="form" method="POST">{{ form.hidden_tag() }}{{ wtf.form_field(form.title) }}{{ wtf.form_field(form.content) }}...{{ wtf.form_field(form.submit) }} </form>注意,如果有多個隱藏字段,可以使用form.hidden_tag()渲染所以隱藏字段。
另外Flask-WTF支持跨站請求偽造保護,表單類創建時會自動創建一個CSRF字段,你需要在表單里渲染這個字段:{{ form.csrf_token }}。
還有一種快速渲染方式:
?
一般渲染(常用)
直接在對應的html模版中引入字段:
{% extends "base.html" %} {% block title %}Flask{% endblock %} {% block body %} <form class="form" method="POST">{{ form.hidden_tag() }}{{ form.title.label }}{{ form.title() }}{{ form.content.label }}{{ form.content() }}{{ form.submit() }} </form> {% endblock %} <html> <head>{% block head %}<title>{% block title %}{% endblock %} - My Application</title>{% endblock %} </head> <body>{% block body %}{% endblock %} </body> </html> base.html另外還可以在字段中加入有些屬性,比如要加入class:
form.title(class_="form-control")轉化為html的效果:
<input type="text" name="title" value="title" class="form-control" id="title" />上面的渲染如果有很多字段的話,一個個寫出來會覺得很繁瑣,在實際開發中,可以創建一個_form.html文件來處理。
_form.html:
接著在對應的html模版中進行引用 :
{% import "_form.html" as forms %}<!--引入_form--><form method="POST" action="">{{ forms.render(form) }}<!--使用_form對form來進行渲染-->?
?一個簡單的例子
from flask import Flask,render_template from flask_wtf import FlaskForm from wtforms import StringField, SubmitField from flask_bootstrap import Bootstrap from wtforms.validators import Requiredapp = Flask(__name__) app.config["SECRET_KEY"] = "12345678" bootstrap = Bootstrap(app)class NameForm(FlaskForm):name = StringField('What is your name?', validators=[Required()])submit = SubmitField('Submit')@app.route('/', methods=['GET', 'POST']) def index():name = None#實例化表單類form = NameForm()# 如果提交的數據驗證通過,則返回Trueif form.validate_on_submit():name = form.name.dataform.name.data = ''return render_template('index.html', form=form, name=name)if __name__ == '__main__':app.run(debug=True)# Flask提供的render_template函數把Jinja2模板引擎集成到了程序中。# render_template函數的第一個參數是模板的文件名。# 隨后的參數都是鍵值對,表示模板中變量對應的真實值。?
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky{% endblock %} {% block page_content %} <div class="page-header"> <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div> {{ wtf.quick_form(form) }} {% endblock %} index.html {% extends "bootstrap/base.html" %} {% block title %}Flasky{% endblock %} {% block navbar %} <div class="navbar navbar-inverse" role="navigation"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle"data-toggle="collapse" data-target=".navbar-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="/">Flasky</a></div><div class="navbar-collapse collapse"><ul class="nav navbar-nav"><li><a href="/">Home</a></li></ul></div></div> </div> {% endblock %} {% block content %} <div class="container">{% block page_content %}{% endblock %} </div> {% endblock %} base.html?
?
重定向和用戶會話
from flask import Flask, render_template, session, redirect, url_for @app.route('/', methods=['GET', 'POST']) def index():form = NameForm()if form.validate_on_submit():session['name'] = form.name.datareturn redirect(url_for('index'))return render_template('index.html', form=form, name=session.get('name')) 前面到的代碼存在一個可用性問題。用戶輸入名字后提交表單,然后點擊瀏覽器的刷 新按鈕,會看到一個莫名其妙的警告,要求在再次提交表單之前進行確認。之所以出現這 種情況,是因為刷新頁面時瀏覽器會重新發送之前已經發送過的最后一個請求。如果這個 請求是一個包含表單數據的 POST 請求,刷新頁面后會再次提交表單。大多數情況下,這并 不是理想的處理方式。 很多用戶都不理解瀏覽器發出的這個警告。基于這個原因,最好別讓 Web 程序把 POST 請 求作為瀏覽器發送的最后一個請求。 這種需求的實現方式是,使用重定向作為 POST 請求的響應,而不是使用常規響應。重定 向是一種特殊的響應,響應內容是 URL,而不是包含 HTML 代碼的字符串。瀏覽器收到 這種響應時,會向重定向的 URL 發起 GET 請求,顯示頁面的內容。這個頁面的加載可能 要多花幾微秒,因為要先把第二個請求發給服務器。除此之外,用戶不會察覺到有什么不 同。現在,最后一個請求是 GET 請求,所以刷新命令能像預期的那樣正常使用了。這個技 巧稱為 Post/ 重定向 /Get 模式。 但這種方法會帶來另一個問題。程序處理 POST 請求時,使用 form.name.data 獲取用戶輸 入的名字,可是一旦這個請求結束,數據也就丟失了。因為這個 POST 請求使用重定向處 理,所以程序需要保存輸入的名字,這樣重定向后的請求才能獲得并使用這個名字,從而構建真正的響應。 程序可以把數據存儲在用戶會話中,在請求之間“記住”數據。用戶會話是一種私有存 儲,存在于每個連接到服務器的客戶端中。用戶會話,它是請求上下文中的變量,名為 session ,像標準的 Python 字典一樣操作。 注意:默認情況下,用戶會話保存在客戶端 cookie 中,使用設置的 SECRET_KEY 進 行加密簽名。如果篡改了 cookie 中的內容,簽名就會失效,會話也會隨之 失效。代碼分析
局部變量 name 被用于存儲用戶在表單中輸入的名字。這個變量現 在保存在用戶會話中,即 session['name'] ,所以在兩次請求之間也能記住輸入的值。 現在,包含合法表單數據的請求最后會調用 redirect() 函數。 redirect() 是個輔助函數, 用來生成 HTTP 重定向響應。 redirect() 函數的參數是重定向的 URL,這里使用的重定向 URL 是程序的根地址,因此重定向響應本可以寫得更簡單一些,寫成 redirect('/') ,但 卻會使用 Flask 提供的 URL 生成函數 url_for() 。推薦使用 url_for() 生成 URL,因為這 個函數使用 URL 映射生成 URL,從而保證 URL 和定義的路由兼容,而且修改路由名字后 依然可用。 url_for() 函數的第一個且唯一必須指定的參數是端點名,即路由的內部名字。默認情 況下,路由的端點是相應視圖函數的名字。在這個示例中,處理根地址的視圖函數是 index() ,因此傳給 url_for() 函數的名字是 index 。 最后一處改動位于 render_function() 函數中,使用 session.get('name') 直接從會話中讀 取 name 參數的值。和普通的字典一樣,這里使用 get() 獲取字典中鍵對應的值以避免未找 到鍵的異常情況,因為對于不存在的鍵, get() 會返回默認值 None 。?
參考鏈接:
1.https://greyli.com/flask-form-create-and-render/
2.WTForms官方文檔
3.http://flask.pocoo.org/docs/1.0/patterns/wtforms/
4.https://stackoverflow.com/questions/20905188/flask-wtforms-validation-always-false 5.《FlaskWeb開發:基于python的Web應用開發實戰
?
轉載于:https://www.cnblogs.com/-wenli/p/11042961.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Flask开发系列之Web表单的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装源码包(这里主要写了redis,其他
- 下一篇: nohub 将程序永久运行下去