Jinja2
Jinja2是Python Web編程中的主流模板語言。因為flask是基於Jinja2和Werkzeug發展而來,在安裝flask的時候jinja2自動就被裝在上面了。
之前提到了很多Jinja2的使用方法啊,下面將補充或者更加詳細地說明Jinja2作為模板語言的使用。
【Jinja2官方文檔】http://docs.jinkan.org/docs/jinja2/
■ 基本語義
看過之前模板的例子就知道,Jinja2做構成的模板文件中,文本內容大致可以分成幾個種類。比如特殊文本(不進行轉義,比如HTML,XML格式的文本)、{{ }}表示的變量或者宏調用、{% %}表示邏輯控制,{# #}表示注釋,其中內容不被模板引擎轉義。
■ set語句賦值
在之前的所有例子中,模板中變量的值似乎只有一個來源,就是來自於后端的提供。但是實際上我們可以在模板這個層面對變量進行賦值,就是通過了{% set variable_name="value" %}的形式。這賦予了模板在內部進行變量賦值的能力,提高了靈活性。如果想要set一個變量的值為某個函數的返回值的話,那么那個函數不用{{ }}包着。比如{% set action_view = url_for('main.upload') %}而不是{{ url_for('main.upload') }}。
■ 過濾器
Jinja2可以使用過濾器,對傳入模板的第一手數據進行二次加工。過濾器的形式是以管道符號連接第一手數據和二次處理數據的方法。比如在模板中寫:
{{ my_variable|default("my_variable is not defined") }}
的意思就是說在此處的第一手數據是由后端傳過來的my_variable這個變量的值。如果這個變量沒有被定義,那么就有default過濾器方法指定了它沒有被定義時應該顯示什么東西。過濾器方法有很多很多,各種各樣的過濾器方法使得數據從后端傳入前段之后還能再進行靈活的操作,從而提高了整個編程的便利性和敏捷性。其他常用或者我看着比較有用的過濾器方法還有:
****以下說明中的第一個參數都是指通過管道傳過去的值,實際寫在管道右邊的方法參數應該比下面說明中的少一個,就像python面向對象編程中的self參數那樣的感覺****
attr(object,name) 比如account|attr("name")中account預期是一個字典(或者按照JS的說法是一個object),此時這個地方就是現實account['name']的值
capitalize(s) 把字符串的首字母大寫,后面的全部小寫
default(value,default_value=u'',boolean=False) 設置默認值,如果沒有給出value,那么就用default_value的值(即空字符串)
filesizeformat(value,binary=False) 接受一個數值然后轉化成容易閱讀的字節形式比如1.3MB,305Byte等,比如{{ size|filesizeformat() }}
float(value,default=0) 接受一個值並將其轉化為float類型
escape(string) 把字符串中HTML特殊字符比如<,>,&等反轉義成HTML的表達方式
groupby(value,attribute) 按照指定的共有屬性將(一個字典組成的列表)集合進行分組,返回一個字典組成的列表。這個列表中的每個字典都有兩項,第一項的key是grouper然后value是根據groupby區分后得到的那個原字典中key的值。說了一串自己都暈了,下面給個例子。。
比如模板是這個樣子的:
<ul> {% for group in persons|groupby('gender') %} <li>{{ group.grouper }}<ul> {% for person in group.list %} <li>{{ person.first_name }} {{ person.last_name }}</li> {% endfor %}</ul></li> {% endfor %} </ul>
而作為persons傳入的數據lst是這個樣子的:
lst = [] lst.append({"gender":"male","first_name":"Frank","last_name":"Takanashi"}) lst.append({"gender":"female","first_name":"Bob","last_name":"Kazuya"}) lst.append({"gender":"female","first_name":"Ocean","last_name":"King"})
那么最終得到渲染完成的文件是長這個樣子的:
<ul> <li>female <ul> <li>Bob Kazuya</li> <li>Ocean King</li> </ul> </li> <li>male <ul> <li>Frank Takanashi</li> </ul> </li> </ul>
int(value,default) 把值轉化為一個int類型
join(value,sep=u'') 接受一個序列類型的對象,然后通過sep指定的字符將所有序列元素連接在一起成為一個字符串。sep默認是空字符串
last/first/random(seq) 返回序列的最后/最前/隨機一個元素
replace(string,old,new,count=None) 接受一個字符串,將其中的old部分全部轉化為new,從左到右替換count次,如果不指定count是只替換第一個掃描到的
reverse(value) 接受一個可迭代對象,然后返回其反序序列的迭代器
sort(value,reverse=False,case_sensative=False,attribute=None) 對可迭代的對象進行排序,默認情況下以升序大小寫不敏感的方式排序。
striptags(value) 把一段比如XML格式的文本中的標簽都去掉
sum(iterable,attribute=None,start=0) 對可迭代對象進行求和,如果需要對其某個特定屬性的值求和的話可以設置attribute屬性
title(string) 將字符串轉換為標題格式顯示
trim(value) 去除開頭和末尾的所有空格,相當於strip()
truncate(s,length=255,killwords=False,end='...') 這是個比較有有意思的方法,它把字符串轉化成簡略形式,比如"foo bar"|truncate(5)可以得到foo ...,如果"foo bar"|truncate(5,True)可以得到foo b...
round(value,precision=0,method='common') 對數字進行四舍五入,precision表示小數點后保留幾位。method除了common是四舍五入規則外,floor是向下抹平,ceil是向上抹平。
* 今天遇到了round的一個坑,就是需要注意round前面的東西一定要是一個整體。當然1.23|round之類,直接寫數字沒問題,當使用算式時請注意一定要把算式用括號括起來才能使過濾器有效。比如 num/1024.0 | round是不會有四舍五入之類的效果的,只有(num/1024.0) | round才行。
wordcount(s) 計算字符串中單詞的個數
以上還不是過濾器方法的全部,一般而言這些處理都可以放在python中做,python也可以做的非常好,但是這樣的話勢必要往模板中傳遞很多很多變量。如果可以在模板這個層面對這些數據進行一些二次處理的話就可以讓后端和前端之間的交互體量變小,提高編程的便利性。
■ 邏輯結構
Jinja2中和一般編程語言中一樣,可以有if/else,for,is等等關鍵字作為邏輯控制的節點。而這些關鍵字必須是放在{% %}或者{{ }}里面的。
● is關鍵字判斷條件
利用is關鍵字和所謂的“測試方法”可以獲得一個布爾值,從而利用其進行一些邏輯判斷。比如{{ name is defined }}可以返回True/False來體現name這個變量是否被定義了。類似的“測試方法”還有:
callable(object) 檢查一個對象是否可調用
defined/undefined(object) 檢查一個對象是/否被定義了
divisibleby(value,num) 檢查value能否被數字num整除
escaped(value) 檢查一個對象是否被轉碼了
iterable(value) 檢查一個對象是否可迭代
lower/upper(value) 檢查一個對象是否都小/大寫
none(value) 檢查一個對象是否是None
number(value) 檢查一個對象是否是數字
sameas(value,other) 檢查一個對象是否和other這個對象是同一個對象
用法:{% if loop.index is divisibleby(3) %}就表示如果loop.index這個值能夠被3整除的話就進入這個邏輯分支
除了上面這些帶參數的判斷條件,也有一些不帶參數的條件,在Jinja2里被稱為測試器(test)。比如:
defined/none 檢查一個對象是否被定義,其實效果和{% if var %}是一樣的
upper/lower 檢查一個字符串對象是否都是大寫或小寫
string {% if var is string %}檢查對象是不是一個字符串
odd/even 檢查一個整數對象是否是奇/偶數
iterable 檢查一個對象是不是可迭代對象,簡單來說就是看是不是列表之類的序列類型
mapping 檢查一個對象是不是k-v對形式的對象,比如字典
● 自定義測試器
如果內置的一些測試器不能滿足我們的需求,那么我們可以自定義一些測試器。具體的做法是在python代碼中寫一個測試函數,然后用app.add_template_test方法把這個測試器和app對象關聯起來。比如下面這個:
def over_five(string): #接受的一定是一個字符串對象,然后可以在函數中對這個字符串做出處理 return int(string) > 5 app.add_template_test(over_five,'my_test') ''' 然后在模板中就可以這么用了: {% if num is my_test %} <p>{{ num }} is bigger than 5</p> {% else %} <p>{{ num }} is smaller than 5</p> {% endif %} '''
● 判斷語句
判斷語句沒什么好說的了,反正就是{% if ... %}{% elif ... %}{% else %}{% endif %}的格式。
● 循環語句
Jinja2中的循環只能通過for語句,而且不具備break,continue之類的循環控制功能。但是在for循環的作用域內,可以訪問特殊對象loop的一些屬性來獲得循環的一些信息。loop具有以下屬性:
loop.index 當前循環迭代的次數(從1開始計數)
loop.index0 從0開始計數的循環迭代次數
loop.revindex 到循環結束為止需要迭代的次數
loop.first 是否是第一次迭代
loop.last 是否是最后一次迭代
loop.length 序列中項目的數量
loop.cycle 在一串序列間取值的輔助函數
比如下面這個代碼的用例:
{% for user in users %} <li>
{% if not loop.first %} {{ user.username }} {% endif %} </li> {% endfor %}
就是說假如是循環中的第一個,就不顯示它的username了。
■ 模板繼承
之前在簡介中也說過好多次,jinja2的模板是可以進行繼承的。采用關鍵字{% extends "parent.html" %}的形式。
父模板中可以有多個不重名的{% block xxx %}{% endblock %},在子模板中通過{% block xxx %}可以指定修改某個block中的內容。更加詳盡的內容都寫在flask的大概介紹里,就不重復了。
■ 獨立使用jinja2
jinja2作為一個插件很廣泛地用於flask,django等框架中。但是也不能忘記,jinja2本身就是一個python的模塊。在非框架的環境中,我們也可以通過其API單獨進行jinja2的調用。常用的方法是這樣子的:
from jinja2 import Template t = Template('Hello, {{ name }}') print t.render(name='Frank') # 輸出 Hello, Frank
可以看到,通過給Template類傳遞一段模板代碼,然后調用其render方法,並且給出模板中變量的定義,就可以得到渲染出來的代碼了。但是這樣有一點不好就是要把模板內容全部寫出來,最好是有傳遞文件名來指定一個模板的。這就要用到jinja2中的Envrionment和Loader兩個概念:
from django import Environment,FileSystemLoader env = Environment(loader=FileSystemLoader('/path/to/directory')) t = env.get_template('test.html') print t.render(name='Frank') # 將模板文件test.html按照給出的參數進行渲染
/path/to/directory是test.html所在的目錄,該目錄下所有其他文件也可以通過get_template方法來作為一個模板文件獲取出來。這里直接寫模板文件名說明模板文件就在目錄下,如果有多層級目錄也可以寫/path/to/file.html。FileSystemLoader應該是一個常用的Loader,它將某個指定的目錄作為模板的存放目錄加入環境中。除了FileSystemLoader以外,還有其他的一些Loader比如PackageLoader,PackageLoader('application','templates')將包application(中有__init__.py文件)中的templates目錄作為模板的存放目錄。
模板內容渲染成實際代碼的邏輯和過程和框架中的jinja2是一樣的。