溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

《Flask Web開發(fā):基于Python的Web應(yīng)用開發(fā)實(shí)戰(zhàn)》筆記二、

發(fā)布時間:2020-03-30 15:59:12 來源:網(wǎng)絡(luò) 閱讀:1357 作者:林樹楷 欄目:編程語言

第三章、模板

?視圖函數(shù)作用即生成請求的響應(yīng),如果把業(yè)務(wù)邏輯和表現(xiàn)邏輯混在一起會導(dǎo)致代碼難以理解和維護(hù)。吧表現(xiàn)邏輯轉(zhuǎn)移到模板中能夠提升程序的可維護(hù)性。
?模板是一個響應(yīng)文本的文件,其中包含用占位變量表示的動態(tài)部分,其具體值只在請求的上下文才能知道。
?使用真實(shí)值替換變量,在返回最終得到的響應(yīng)字符串,這一過程稱為渲染。

3.1、Jinja2模板引擎

3.1.1、渲染模板

?在默認(rèn)情況下,F(xiàn)lask程序會在templates子文件夾中尋找模板。在下一個hello.py版本中,要把前面定義的模板保存在templates文件夾中,并分別命名為index.html和user.html。

from flask import Flask,render_template
from flask_script import Manager

app = Flask(__name__)
manager = Manager( app )

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/user/<name>')
def user(name):
    return render_template('user.html',name=name)

if __name__ == "__main__":
   manager.run()
  • ?代碼詳解:Flask提供的render_template函數(shù)吧Jinja2模板引擎集成到程序中。render_template函數(shù)的第一個參數(shù)是模板的文件名,隨后的參數(shù)都是鍵值對,表示模板中變量對應(yīng)的真實(shí)值。

3.1.2、變量

  • ?Jinja2能識別所有類型的變量,甚至是一些復(fù)雜的類型,例如列表、字典和對象。在模板中使用變量的一些示例如下:

    <p>DICT {{ mydict['key'] }}</p>
    <p>LIST {{ mylist[3] }}</p>
    <p>list with a variable index: {{ mylist[myintvar]}}</p>
    <p> object's method: {{ myobj.somemethod() }} </p>
  • 常用Jinja2 變量過濾器
  1. safe 渲染值時不轉(zhuǎn)義
  2. capitalize 把值的首字母轉(zhuǎn)換成大寫,其他字母轉(zhuǎn)換成小寫
  3. lower 把值轉(zhuǎn)換成小寫形式
  4. upper 把值轉(zhuǎn)換成大寫形式
  5. title 把值中每個單詞的首字母都轉(zhuǎn)換成大寫
  6. trim 把值的首尾空格去掉
  7. striptags 渲染之前把值中所有的HTML標(biāo)簽都刪掉

3.1.3、控制結(jié)構(gòu)

條件控制語句
{% if user %}
{% else %}
{% endif %}
for循環(huán)語句
{% for comment in comments %}
{% endfor %}
支持宏
{% marco render_comment(comment) %}
多處重復(fù)使用的模板代碼片段可以寫入單獨(dú)的文件,再包含在所有的模板中,以避免重復(fù):
{ % include 'comment.html' %}

  • ?另外一中重復(fù)使用代碼的強(qiáng)大方式是模板繼承,他類似于Python代碼中的類繼承。繼承方式如下:首先創(chuàng)建一個名為base.html的基礎(chǔ)模板:
    <html>
    <head>
    {% block head %}
    <title>
    {% block title %}
    {% endblock %}
    - My Application
    </title>
    {% endblock %}
    </head>
    <body>
    {% block body %}
    {% endblock %}
    </body>
    </html>    
  • ?block標(biāo)簽定義的元素可在衍生的模板中年修改。在本例中,我們定義了名為head,title,body的塊元素。注意,title包含在head中。下面就是基于基礎(chǔ)模板的衍生模板:
    {% extends bash.html %}
    {% block title %}
    Index
    {% endblock%}
    {% block head%}
    {{ super() }}
    <style>
    </style>
    {% endblock %}
    {% block body %}
    <h2>hello,world</h2>
    {% endblock %}
  • ?extends指令聲明這個模板衍生自base.htmk,在extends指令之后,基礎(chǔ)模板中的3個板塊重新定義,模板引擎會賈汪其插入適當(dāng)?shù)奈恢?。注意新定義head塊,在基礎(chǔ)模板中內(nèi)容是空的,所以使用super()獲取原來的內(nèi)容。

3.2、使用Flask-Bootstrap集成Twitter Bootstrap

? Bootstrap是Twitter開發(fā)的一個開源框架,它提供的用戶界面組件可用于創(chuàng)建整潔且具有吸引力的網(wǎng)頁,并且這些網(wǎng)頁還能兼容所有現(xiàn)代的Web瀏覽器。
?Bootstrap是客戶端框架,不會直接涉及服務(wù)器。要下在程序中繼承Bootstrap,顯然需要對模板做所有必要的改動,更簡單的方法就是安裝Flask-Bootstrap的Flask擴(kuò)展,簡化集成的過程。

  • Flask-Bootstrap使用pip方式安裝
    pip install flask-bootstrap
  • Flask擴(kuò)展一般在創(chuàng)建程序?qū)嵗龝r初始化。

    from flask.ext.bootstrap import Bootstrap
    bootstrap = Bootstrap(app)
  • templates/user.html,使用的就是Flask-Bootstrap的模板
    {% 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">
    <div class="page-header">
    <h2>Hello, {{ name }}!</h2>
    </div>
    </div>
    {% endblock %}

代碼詳解:

  • ?Jinja2中的extends指令從Flask-Bootstrap中導(dǎo)入bootstrap/base.html,從而實(shí)現(xiàn)模板繼承。Flask-Bootstrap中的基礎(chǔ)模板提供了一個網(wǎng)頁框架,引入了Bootstrap中的所有CSS和JavaScript文件。
  • ? 基礎(chǔ)模板中定義了可在衍生模板中重新定義的塊。block和endblock指令定義塊中的內(nèi)容可添加到基模板中。

Flaks-Bootstrap基模板中定義的塊:
塊名 說明

  1. doc 整個HTML文檔
  2. html_attribs <html>標(biāo)簽的屬性
  3. html <html>標(biāo)簽的內(nèi)容
  4. head <head>標(biāo)簽中的內(nèi)容
  5. title <title>標(biāo)簽中的內(nèi)容
  6. metas 一組<meta>標(biāo)簽
  7. styles 層疊樣式表定義
  8. body_attribs <body>標(biāo)簽的屬性
  9. body <body>標(biāo)簽中的內(nèi)容
  10. navbar 用戶定義的導(dǎo)航條
  11. content 用戶定義的頁面內(nèi)容
  12. scripts 文檔底部的JavaScript聲明

3.3、自定義錯誤頁面

Flask允許程序使用基于模板的自定義錯誤頁面,最常見的錯誤代碼有兩個:
404,客戶端請求未知頁面或路由時顯示。
500,有未處理的異常時顯示。

  • 自定義錯誤頁面:
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'),404

@app.errorhandler(500)
def internal_server_error(e):
    return render_templte('500.html'),500    
  • ? 和視圖函數(shù)一樣,錯誤處理程序也返回響應(yīng),它們還返回與該錯誤對應(yīng)的數(shù)字狀態(tài)碼。
  • ? 如果從Github上克隆了這個程序的Git倉庫,執(zhí)行g(shù)it checkout 3c簽出這個程序的這個版本。

3.4、鏈接

?在模板中直接編寫簡單路由的URL連接不難,但對于包含可變部分的動態(tài)路由,在模板中構(gòu)建正確的URL就很困難;并且直接編寫URL會對代碼中定義的路由產(chǎn)生不必要的依賴。
?Flask提供了url_for()輔助函數(shù),可以使用程序URL映射中保存的信息生成URL。
?url_for()函數(shù)最簡單的用法是以視圖函數(shù)名(后者app.add_url_route()定義路由時使用的端點(diǎn)名)作為參數(shù),返回對應(yīng)的URL。
?使用url_for()生成動態(tài)地址時,將動態(tài)部分作為關(guān)鍵字參數(shù)傳入。例如,url_for('user',name='john',_external=True)的返回結(jié)果是http://localhost:5000/user/john
?傳入url_for()的關(guān)鍵字參數(shù)不僅限于動態(tài)路由中的參數(shù)。函數(shù)能將任何額外參數(shù)添加到查詢字符串中。例如,url_for('index',page=2)的返回結(jié)果是/?page=2

3.5、靜態(tài)文件

?默認(rèn)設(shè)置下,F(xiàn)lask在程序根目錄中名為static的子目錄中尋找靜態(tài)文件。如果需要,可在static文件夾中使用子文件夾存放文件。

3.6、使用Flask-Monment本地化日期和時間

?問題背景:如果Web程序的用戶來自世界各地,那么處理日期和時間就不是一個簡單的任務(wù)。
?解決方法:通過使用JavaScript開發(fā)的優(yōu)秀客戶端開源代碼庫,名為moment.js,可以在瀏覽器中渲染日期和時間。Flask-Monment是一個Flask程序擴(kuò)展。能把moment.js集成到Jinja2模板中。

  • Flask-Moment可以通過pip安裝:

    pip install flask-moment
  • 初始化Flask-Moment:
    from flask.ext.moment import Moment
    moment = Moment(app)
  • 除了moment.js,F(xiàn)lask-Moment還依賴jquery.js.Bootstrap已經(jīng)引入了jquery.js,因此只需引入moment.js即可。
    {% block scripts %}
    {{ super() }}
    {{ moment.include_moment() }}
    {% endblcok %}
  • 為了處理時間戳,F(xiàn)lask-Moment想模板開放了moment類,后臺可以將utc時間傳到前臺進(jìn)行渲染。
    from datetime improt datetime
    @app.route('/')
    def index():
    return render_template('index.html',current_time=datetime.utcnow())
  • 在模板中渲染current_time
    <p>The local date and time is {{ moment(current_time).format('LLL) }}</p>
    <p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>

更多moment.js用法:http://momentjs.com/docs/#/displaying/
Flask-Moment假定服務(wù)器端程序處理的時間是“純正的”datetime對象,且使用UTC表示。

第四章、Web表單

?對于一些重復(fù)操作(生成表單的HTML代碼和驗(yàn)證提交的表單數(shù)據(jù)),F(xiàn)lask-WTF擴(kuò)展可以把處理Web表單的過程變成一種愉悅的體驗(yàn)。這個擴(kuò)展對獨(dú)立的WTForms包進(jìn)行了包裝,方便集成到Flask程序中。
WTForms官網(wǎng):http://wtforms.simplecodes.com

  • Flask-WTF及其依賴可使用pip安裝:
    pip install flaks-wtf

4.1、跨站請求偽造保護(hù)

?默認(rèn)情況,F(xiàn)lask-WTF能保護(hù)所有表單面授跨站請求偽造(CSRF)的***。為了實(shí)現(xiàn)CSRF保護(hù),F(xiàn)lask-WTF需要程序設(shè)置一個密鑰。Flask-WTF使用這個密鑰生成加密令牌,再用令牌驗(yàn)證請求中表單數(shù)據(jù)的真?zhèn)巍?/p>

示例代碼(設(shè)置Flask-WTF):

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
  • ?代碼詳解:app.config字典可用來存儲框架、擴(kuò)展和程序本身的配置變量
  • ?SECRET_KEY配置變量是通用密鑰,可在Flask和多個第三方擴(kuò)展中使用。

4.2、表單類

使用Flask-WTF時,每個Web表單都由一個繼承自Form的類表示。

示例代碼(一個簡單的Web表單,包含一個文本字段和一個提交按鈕):

from flaks.ext.wtf import Form
from wtforms import StringField,SubmitField
from wtforms.validators import Required

class NameForm(Form):
    name = StringField("What's your name?",validators=[Required()])
    submit = SubmitField('Submit')
  • ?代碼詳解:StringField類表示屬性為type="text"的<input>元素;SubmitField類表示屬性為type="submit"的<input>元素。StringField構(gòu)造函數(shù)中的可選參數(shù)validators指定一個由驗(yàn)證函數(shù)組成的列表,在接收用戶提交的數(shù)據(jù)之前驗(yàn)證數(shù)據(jù)。驗(yàn)證函數(shù)Required()確保提交的字段不為空
  • ?Form基類由Flask-WTF擴(kuò)展定義,從falsk.ext.wtf中導(dǎo)入。字段和驗(yàn)證函數(shù)可以直接從WTForms包中導(dǎo)入。

WTForms支持的HTML標(biāo)準(zhǔn)字段

  1. StringField 文本字段
  2. TextAreaField 多行文本字段
  3. PasswordField 密碼文本字段
  4. HiddenField 隱藏文本字段
  5. DateField 文本字段,值為 datetime.date 格式
  6. DateTimeField 文本字段,值為 datetime.datetime 格式
  7. IntegerField 文本字段,值為整數(shù)
  8. DecimalField 文本字段,值為 decimal.Decimal
  9. FloatField 文本字段,值為浮點(diǎn)數(shù)
  10. BooleanField 復(fù)選框,值為 True 和 False
  11. RadioField 一組單選框
  12. SelectField 下拉列表
  13. SelectMultipleField 下拉列表,可選擇多個值
  14. FileField 文件上傳字段
  15. SubmitField 表單提交按鈕
  16. FormField 把表單作為字段嵌入另一個表單
  17. FieldList 一組指定類型的字段

WTForms驗(yàn)證函數(shù)

  1. Email 驗(yàn)證電子郵件地址
  2. EqualTo 比較兩個字段的值;常用于要求輸入兩次密碼進(jìn)行確認(rèn)的情況
  3. IPAddress 驗(yàn)證 IPv4 網(wǎng)絡(luò)地址
  4. Length 驗(yàn)證輸入字符串的長度
  5. NumberRange 驗(yàn)證輸入的值在數(shù)字范圍內(nèi)
  6. Optional 無輸入值時跳過其他驗(yàn)證函數(shù)
  7. Required 確保字段中有數(shù)據(jù)
  8. Regexp 使用正則表達(dá)式驗(yàn)證輸入值
  9. URL 驗(yàn)證 URL
  10. AnyOf 確保輸入值在可選值列表中
  11. NoneOf 確保輸入值不在可選值列表中

4.3、把表單渲染成HTML

示例代碼:(使用Flask-WTF和Flask-Bootstrap渲染表單)

{% extends 'base.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
<h2>hello,{% if name %}{{ name }}{% else %}Stranger{% endif%}</h2>
</div>
{{ wtf.quick_form(form)}}
{% endblock %}
  • ? 代碼詳解:導(dǎo)入的bootstrap/wtf.html文件定義了一個使用Bootstrap渲染Flask-WTF表單對象的輔助函數(shù)。wtf.quick_form()函數(shù)的參數(shù)為Flask_WTF表單對象,使用Bootstrap的默認(rèn)樣式渲染傳入表單。

4.4、在視圖函數(shù)中處理表單

示例代碼:(視圖函數(shù)index()不僅要渲染表單,還要接受表單中的數(shù)據(jù)。)

@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('index.html',form=form,name=name)    
  • 代碼詳解:app.route修飾器中添加的methods參數(shù)告訴Flask在URL映射中把這個視圖函數(shù)注冊為GET和POST請求的處理程序。如果沒指定methods參數(shù),則默認(rèn)把視圖函數(shù)注冊為GET請求的處理程序。
  • ?用戶提交表單后,服務(wù)器會收到一個POST請求。validate_on_submit()會調(diào)用name字段上附屬的Required()驗(yàn)證函數(shù)。如果名字不為空,就能通過驗(yàn)證,validate_on_submit()返回True。

4.5、重定向和用戶會話

?問題背景:當(dāng)用戶輸入名字后提交表單,再點(diǎn)擊瀏覽器的刷新按鈕,會看到一個警告,關(guān)于要求再次提交表單之前進(jìn)行確認(rèn)。之所以會出這種問題,是因?yàn)樗⑿马撁鏁r瀏覽器會重新發(fā)送之前已經(jīng)發(fā)送過的最后一個請求。
?解決方案:使用重定向作為POST請求的響應(yīng),而不是使用常規(guī)響應(yīng)。重定向是一種特殊的響應(yīng),響應(yīng)內(nèi)容是URL,而不是包含HTML代碼的字符串。瀏覽器收到這種響應(yīng),會向重定向的URL發(fā)起GET請求,顯示頁面的內(nèi)容。
?另一問題:如果使用上面的解決方案,程序在處理POST請求時,使用from.name.data獲取用戶輸入的名字,一旦請求結(jié)束,數(shù)據(jù)也就丟失了。所以需要程序?qū)?shù)據(jù)存儲到用戶會話中,在請求之間“記住”數(shù)據(jù)。用戶是一種私有存儲,存在每個連接到服務(wù)器的客戶端中。

示例代碼:

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.data
        return redirect(url_for('index'))
    return render_template('index.html',form=form,name=session.get('name')))    
  • ?代碼詳解:合法表單數(shù)據(jù)的請求最后會調(diào)用redirect()函數(shù)。redirect()是個輔助函數(shù),用來生成HTTP重定向響應(yīng)。redirect()函數(shù)參數(shù)是重定向的URL。url_for()生成URL,因?yàn)檫@個函數(shù)使用URL映射生成URL,從而保證URL和定義的路由兼容,并且修改路由名字后依然可用。

4.6、Flash消息

?問題背景:用戶提交了有一項(xiàng)錯誤的登錄表單,服務(wù)器發(fā)回的響應(yīng)重新渲染了登錄表單,并在表單上面顯示信息,提示用戶名或密碼錯誤。

示例代碼:

from flask ipmort Flask,render_template,session,redirect,url_for,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 you have changed your name")
       session['name'] = olde_name
       return redirect(url_for('index'))
   return render_template('index.html',name=session.get('name'),form=form)    ```

* ?代碼詳解:代碼會將每一次提交的名字與上一次**存儲在會話中的名字**進(jìn)行比較,如果兩者不一樣則會發(fā)給客戶端下一個響應(yīng)中顯示一個信息。
* ?僅調(diào)用flash()函數(shù)并不能把消息顯示出來,程序使用的模板要渲染這些信息,最好在基礎(chǔ)模板中渲染Flash消息,因?yàn)檫@樣所有頁面都能使用這些消息。Flask把**get_flashed_messages()函數(shù)**開放給模板,用來獲取并渲染消息。

{% 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 %}

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI