第21天:Web開發 Jinja2模板引擎

在以前的文章中,簡單介紹了Python Web開發框架Flask,知道了如何寫個Hello World,可是距離用Flask開發真正的項目,還有段距離,如今咱們目標更靠近一些 —— 學習下Jinja2模板。html

模板的做用

模板是用來作什麼的呢?模板是用來更高效地生成相應時的Html文本的,沒有模板,能夠手寫,好比以前的hello world示例,寫段html代碼:python

<h1>Hello world!</h1>

對於簡單的練習還行,但對於規模大的,動態化程度高的項目來講,這樣寫就有些勉強了,即,不利於項目和產品化。那麼模板有什麼好處呢:linux

  1. 能讓展示邏輯和業務邏輯 展現邏輯即UI,就是用來給用戶看和操做的,業務邏輯是業務規則,好比什麼條件能夠註冊,什麼權限能考到什麼。模板將展示邏輯封裝起來,業務邏輯寫在視圖函數中。git

  2. 能使項目更易維護 因爲展示邏輯和業務邏輯的分離,它們能夠由不一樣的開發人員來維護,不會有代碼衝突的問題程序員

  3. 使項目更加安全 在作交互式開發中,有個原則: 永遠不要相信用戶的輸入,由於惡意用戶可能經過輸入來注入(關於注入之後有機會能夠單獨聊聊),而模板在必定程度上會防注入,例如用戶輸入一點html代碼做爲輸入,默認狀況下模板會將其替換爲網絡安全字符,以防止惡意注入。github

  4. 能提升開發效率 有了模板,至關於一個展現邏輯的函數,因此就能夠被複用,能夠用在不一樣的視圖函數中,也能夠用在不一樣的項目中面試

思考下:上面提到的展示邏輯業務邏輯,爲何不直接說成前臺後臺呢? 若是你有答案和想法,歡迎留言討論。編程

Jinja2模板引擎

Jinja2是Flask框架默認支持的模板引擎,並非惟一也不是最好(因人而異,沒有最好)模板引擎,不一樣的Web框架,好比Django、Nodejs等都有本身的模板引擎,甚至一些程序員本身實現的模板引擎(我就這麼幹過),但大致思路是同樣的,都是要將數據替換或者轉換到,用特殊格式標記了位置的模板中,以合成動態的html,這種技術不新鮮,在以前的打印模板,如水晶報表裏就有,無非就是標記和語法不一樣而已,因此要觸類旁通。flask

引入渲染函數

像其餘功能同樣,要使用模板引擎,先引入api

from flask import render_template

注意:要將將模板文件放置在項目根目錄(即 print(__file__)顯示的路徑)下的 templates文件夾中

例如模板文件 hello.html爲:


<h1>Hello {{ name }} </h1>

{% endraw %}

視圖函數能夠寫成:

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

Flask提供的 render_template函數把Jinja2模板引擎集成到了程序中。render_template函數第一個參數是模板的文件名,隨後的參數都是鍵值對,表示模板中變量的對應的真實值,在上面代碼中,模板會接收到一個名爲 name的變量

變量

模板文件就是普通的文本文件,而後將須要替換的部分用雙大括號( {{}})標記出來,雙大括號中,表示要替換的變量名,這個變量支持基本數據類型,以及列表、詞典、對象和元組。如模板 template.html:


<p> A value form a string: {{ name }}.</p><p> A value form a int: {{ myindex }}.</p><p> A value form a list: {{ mylist[3]] }}.</p><p> A value form a list, with a variable index: {{ mylist[myindex] }}.</p><p> A value form a dictionary: {{ mydict['key'] }}.</p><p> A value form a tuple: {{ mytuple }}.</p><p> A value form a tuple by index: {{ mytuple[myindex] }}.</p>

{% endraw %}

視圖函數代碼:

@app.route('/template/')def template():    name = 'Jinja2 模板引擎'    myindex = 1    mylist = [1,2,3,4]    mydict = {       key: 'age',       value: '25' }    mytuple = (1,2,3,4) return render_template('template.html', name=name, myindex=myindex, mylist=mylist, mydict=mydict, mytuple=mytuple)

顯示結果:


過濾器

有些時候須要對要在模板中替換的值作一些特殊處理,好比首字母大寫,去掉先後空格等等,有種選擇就是使用過濾器。

說明

Jinjia2模板引擎中,過濾器相似於Linux命令中的管道,例如將字符串變量的首字母大寫

{% raw %}

<h1>{{ name | capitalize}}</h1>

{% endraw %}

過濾器能夠拼接,和linux的管道命令同樣,如對值進行所有變大寫,而且去除先後空白字符:

{% raw %}

<h1>{{ name | upper | trim }}</h1>

{% endraw %}

如上代碼,過濾器和變量之間用管道符號 | 相連,至關於對變量值做進一步加工。

一些經常使用的過濾器

過濾器 說明
safe 渲染是不轉義
capitalize 首字母大寫
lower 全部字母小寫
upper 全部字母大寫
title 值中每一個單詞首字母大寫
trim 刪除首位空白字符
striptags 渲染時刪除掉值中全部HTML標籤

注意:safe過濾器,默認狀況下,處於安全考慮,Jinja2會轉義全部變量,例如一個變量的值爲 <h1>Hello</h1>, Jinja2會將其渲染成 &lt;h1&gt;Hello&lt;/&gt;,瀏覽器會顯示出本來的值,可是不會解釋。若是須要瀏覽器解釋的話,可使用 safe 過濾器 例如模板文件 html.html爲:

{% raw %}

<h1>{{ html | safe }}</h1>

{% endraw %}

視圖函數爲:

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

注意:千萬別在不可信的值上使用 safe 過濾器,例如用戶在表單上輸入的文本。

還有一些有用的過濾器


  • default,能夠當變量未定義時,提供默認值,若是想將 falseFalse和空( none)視爲未定義,須要提供第二個參數爲 true{% raw %}


    {% endraw %}

    當變量 name的未定義時,上下兩個顯示效果同樣,當值爲 none時,上面會顯示 Hellonone!, 而下面的會顯示 Helloworld!


  1. <!-- 提供默認值過濾器 -->

  2. <h1>Hello {{ name | default('world') }}!</h1>


  3. <!-- 將false、False和空(none)視爲未定義的默認值過濾器 -->

  4. <h1>Hello {{ name | default('world', true)! }}</h1>


列表過濾器 min, max, 獲得列表中的最小值或最大值


自定義過濾器

過濾器雖然有不少,但總有不知足需求的時候,例如首行文字縮進、將金額轉化爲中文的大寫等等。過濾器實質就是個函數,因此,第必定義一個過濾器函數,第二,註冊到Jinjia2的過濾器中。


  1. # 定義過濾器函數

  2. def mylen(arg):# 實現一個能夠求長度的函數

  3. return len(arg)

  4. def interval(test_str, start, end): # 返回字符串中指定區間的內容

  5. return test_str[int(start):int(end)]


  6. # 註冊過濾器

  7. env = app.jinja_env

  8. env.filters['mylen'] = mylen

  9. env.filters['interval'] = interval


  10. # 視圖函數

  11. @app.route('/myfilter')

  12. def myfilter():

  13. return render_template('myfilter.html', phone='13300000000')

模板文件

{% raw %}

<h1>電話號碼是:{{ phone }}, 長度爲:{{ phone | mylen }},運營商號:{{ phone | interval(0,3) }}</h1>

{% endraw %}

過濾器註冊代碼還能夠寫在初始化代碼 __init__.py

控制結構

不少時候,須要更智能的模板渲染,即能給渲染編程,好比男生一個樣式,女生同樣樣式,控制結構指令須要用指令標記來指定,下面介紹下一些簡單的控制結構

條件

即在模板中用 if-else控制結構

{% raw %}

{% if gender=='male' %} Hello, Mr {{ name }}{% else %} Hello, Ms {{ name }}{% endif %}

{% endraw %}

視圖函數

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

在控制結構裏,代碼語法同 python

循環

循環對於渲染列表,頗有幫助,循環的標記是 for。例如獎列表的內容顯示在 ul

{% raw %}

<ul>{% for name in names %} <li>{{ name }} </li>{% endfor %}</ul>

{% endraw %}

例如給定一個學生列表,將其用無序列表 ul顯示出來

宏——模板中的函數

模板中能夠定義宏,至關於定義了一個函數,能夠重複使用,讓邏輯更清晰。首先,定義一個宏:

mymacro.html {% raw %}

{% macro render_name(name) %} <li>{{ name }}</li>{% endmacro %}

{% endraw %} 而後使用宏, 例如將循環結構的例子中,顯示名稱的地方,改成調用宏 {% raw %}

<ul>    {% for name in names %}        {{ render_name(name) }}    {% endfor %}</ul>

{% endraw %}

調用宏,和調用函數是同樣的,不過要將代碼寫在 {{}}雙大括號內。通常咱們會將宏存在單獨的文件中,以便複用,在須要用到宏的地方,引用就行了

{% raw %}

{% import 'mymarco.html' as macros %}<ul> {% for name in names %} {{ macros.render_name(name) }} {% endfor%}</ul>

{% endraw %}

如上所述,用improt引入宏定義文件,經過as指定別名,和python的模塊引入同樣。指定別名是一個良好的編程習慣,能夠將一個複雜的東西形象化,同時像一個命名空間同樣,有效的避免衝突。

include

另外能夠將多個模板片斷寫入一個單獨文件,再包含( include)在全部模板中,以提升開發效率:

{% raw %}

{% include 'common.html' %}

{% endraw %}

include進來的文件,至關於將文件中的內容複製到 include的位置,因此自使用以前須要考慮仔細

模板繼承

若是以爲 include過於呆板,靈活性差,Jinja2模板引擎還有更高級的功能——繼承。相似於Python代碼中類的繼承,一塊兒看看。首先定義一個基類, base.html: {% raw %}

<html><head>    {% block head %} <title>{% block title %}{% endblock%} - My Application</title>    {% endblock %}</head><body>    {% block body %} <h3>這是基類的內容</h3>    {% endblock %}</body></html>

{% endraw %}

其中的 block標籤,定義了能夠被子類重構(替換)的部分,每一個 blcok標籤,須要指定一個特殊的名稱,例如 headtitle等,以便子類用特定的名稱來重構。另外 block標籤須要有結束標籤 endblock,相似於類C語言中的大括號,固然 block標籤也能夠嵌套。接下來,定義一個子類模板 hello3.html:

{% raw %}

{% extends "base.html" %}{% block title %}Index{% endblock %}{% block head %} {{ super() }} <style></style>{% endblock %}{% block body %} {{ super() }} <h3>這是子類的內容 Hello world!</h>{% endblock %}

{% endraw %}

效果如圖所示:


經過 extends標記來指定須要繼承的基類,而後用 block標記來設置子類須要替換調基類中的內容,只要 block指定的名稱同樣就行。另外,如不須要徹底替換調基類的內容,能夠在子類 block中,調用 super方法,以獲取基類在此名稱下的內容,這樣就能達到更號的靈活性。

總結

今天介紹了Jinja2模板引擎的基本用法和特色,指望經過不一樣的特色,讓你瞭解到模板的基本用法,以便更快的使用和進一步學習更深刻的內容。另外,想經過Jinja2模板引擎,說明模板的基本特徵,以便舉一反三、觸類旁通,更快的學習其餘優秀的模板, 同時也想說明,模板不只僅能夠用在Web的開發中,還能夠用在自動化編碼、測試等衆多領域。最後在本章開頭,留了個思考題,爲何不將展示邏輯業務邏輯說成是前臺後臺呢?若是你有答案,歡迎留言交流。

參考

  • 圖書: Flask Web開發 https://item.jd.com/12418677.html

  • API-Jinja Documentation(2,10.x) https://jinja.palletsprojects.com/en/2.10.x/api/#the-context

  • ansible基礎-Jinjia2模板——過濾器 

    https://www.cnblogs.com/mauricewei/p/10056379.html


參考代碼

https://github.com/JustDoPython/python-100-day/tree/master/day-020


系列文章

          LeetCode面試系列 第4天:No.202 - 快樂數



  第11天:Python 字典

第10天:Python 類與對象

第9天:Python Tupple

第8天:Python List

第7天:Python 數據結構--序列

第6天:Python 模塊和

第5天:Python 函數

第4天:Python 流程控制

第3天:Python 變量與數據類型

第2天:Python 基礎語法

第1天:Python 環境搭建

相關文章
相關標籤/搜索