inja2是一個流行的Python模板引擎,它可以幫助開發人員更加高效地構建Web應用程序。在本文中,我們將介紹Jinja2的基礎知識和使用方法,并通過編程示例來演示其實際應用。
Jinja2是一個基于Python的模板引擎,它使用簡單、靈活性高,適用于各種Web應用程序的開發。Jinja2提供了類似于HTML的模板語言,可以動態地生成HTML頁面,并支持循環、條件判斷、變量替換等常用操作。在Jinja2中,我們可以使用{{}}標記表示需要動態生成的內容,并使用if語句和for循環等語句實現條件判斷和循環操作。
下面,讓我們通過一個簡單的示例來了解Jinja2的使用。假設我們需要創建一個Web應用程序,顯示一個包含用戶信息的動態頁面。首先,我們需要定義一個包含用戶信息的Python字典,例如:
users=[
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 30},
{'name': 'Charlie', 'age': 35}
]
接著,我們需要創建一個Jinja2模板,用于生成包含用戶信息的HTML頁面。在模板中,我們可以使用for循環語句遍歷用戶列表,并使用{{}}標記表示需要動態生成的內容。例如,我們可以創建一個名為user.html的模板文件,內容如下:
<!DOCTYPE html>
<html>
<head>
<title>User List</title>
</head>
<body>
<h1>User List</h1>
<ul>
{% for user in users %}
<li>{{ user.name }} - {{ user.age }}</li>
{% endfor %}
</ul>
</body>
</html>
在模板中,我們使用{% for %}和{% endfor %}標記實現循環操作,并使用{{ user.name }}和{{ user.age }}標記表示用戶的姓名和年齡。接著,我們可以通過render_template()函數將模板與路由函數關聯起來,實現動態Web頁面的生成。例如,我們可以創建一個名為app.py的Python文件,內容如下:
from flask import Flask, render_template
app=Flask(__name__)
users=[
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 30},
{'name': 'Charlie', 'age': 35}
]
@app.route('/')
def index():
return render_template('user.html', users=users)
if __name__=='__main__':
app.run()
在該文件中,我們首先導入Flask和render_template函數,并創建一個名為app的Flask應用程序。接著,我們定義一個名為users的列表,包含三個用戶的信息。最后,我們定義一個名為index的路由函數,用于處理用戶的請求。在該函數中,我們使用render_template()函數將user.html模板與路由函數關聯起來,并將用戶列表傳遞給模板。最后,我們通過app.run()函數啟動Flask應用程序,實現Web應用程序的運行。
總之,Jinja2是一個強大的Python模板引擎,可以幫助開發人員更加高效地構建Web應用程序。通過掌握其基礎知識和使用方法,我們可以輕松地實現各種復雜的Web頁面和功能。
紹
cJinja 是一個使用cpp編寫的輕量html模版解析庫,依賴 ejson 來實現模版的數據替換(在jinja中稱為context,上下文)。模版的語法基本與django jinja一致,功能還算豐富。源碼僅有700行,適合學習,覺得不錯的點個star吧。
(該程序為 https://github.com/HuangHongkai/tinyserver 中的一個模塊)
編譯
使用cmake來編譯,windows和linux下均可編譯。推薦使用clion作為IDE。
編譯成功后在build目錄下會有libcjinja.a和cjinja_test.exe這2個文件。libcjinja.a是靜態庫,cjinja_test.exe是一個簡單的測試程序。
運行測試程序后會出現output.html(該文件是tmp.html解析后的結果。)
已經完成的功能
需要注意,表達式之間不能含有空格,例如{{ 1 + 1 }}是非法的,而{{ 1+1 }}是合法的。
使用方法
1. 變量和變量索引
簡單的例子如下,
HtmlTemplate html("username:{{ username }}\n" "parm.list[1][2]: {{parm.list[1][2] }} \n" "parm.key: {{ parm.key }}", 1); // 參數1表示傳入的是模版字符串,0表示傳入的是文件名,默認為0 JSONObject obj={ {"username", 1234}, {"parm", { {"key", "cde"}, {"list", {1, {1,2.3, "abcd"}, "hahaha"}}, }} }; html.setValue(obj); cout << html.render() << endl << endl; /* 運行后打印如下 username:1234 parm.list[1]: abcd parm.key: cde */
HtmlTemplate是一個庫的主要類,構造函數為
explicit HtmlTemplate(const string& str, int flag=0); // flag=0是str表示文件路徑,不為0是表示傳入的模版字符串
其中str參數為字符串,可以表示html模板原始串,也可也表示為文件的路徑,flag默認為0。
setValue 方法表示傳入數據給模版對象。
render() 方法表示將模版解析成字符串。
JSONObject來源于 ejson 庫,用來模擬python的dict,構造函數也比較容易看懂。
2. 列表迭代
HtmlTemplate html("{% for x in list %}{{ x }}\n{%endfor%}" "此時x已經是臨時變量了,不可以在打印了 {{x}}\n" , 1); JSONObject obj=OBJECT( KEYVALUE("list", LIST(1,2,3,4,5)) ); cout << html.setValue(obj).render() << endl << endl; /*運行后輸出如下 1 2 3 4 5 此時x已經是臨時變量了,不可以在打印了 */
注意到在迭代過程中x是作為臨時變量,在外部的話是無法打印出來的。
3. 字典迭代
HtmlTemplate html("{% for key in dict %}迭代1: 字典的key值為 {{ key }}\n{% endfor %}" "{% for key,value in dict %}迭代2: 字典的key值為 {{ key }}, value值為 {{ value}}\n{% endfor %}" "{% for ,value in dict %}迭代3: 字典的value值為 {{ value }}\n{% endfor %}", 1); JSONObject obj=OBJECT( KEYVALUE("dict", OBJECT( KEYVALUE("key1", "value1"), KEYVALUE("key2", 1234), KEYVALUE("key3", nullptr), )) ); cout << html.setValue(obj).render() << endl << endl; /*運行后輸出 迭代1: 字典的key值為 key1 迭代1: 字典的key值為 key2 迭代1: 字典的key值為 key3 迭代2: 字典的key值為 key1, value值為 value1 迭代2: 字典的key值為 key2, value值為 1234 迭代2: 字典的key值為 key3, value值為 null 迭代3: 字典的value值為 value1 迭代3: 字典的value值為 1234 迭代3: 字典的value值為 null */
4. 字符串拼接與表達式計算
HtmlTemplate html("{{ a+b+c+\"444\" }}\n" "{{x}} * {{y}} + 2 * 3 - 4 / {{x}}={{ x*y+2*3-4/x }}\n", 1); JSONObject obj=OBJECT( KEYVALUE("a", "111"), KEYVALUE("b", "222"), KEYVALUE("c", "333"), KEYVALUE("x", 12), KEYVALUE("y", 34) ); cout << html.setValue(obj).render() << endl << endl; /*運行后輸出 111222333444 12 * 34 + 2 * 3 - 4 / 12=413.667 */
5. if-else-endif語句
HtmlTemplate html("{% if 1==1 %} 1==1 成立 {% else %} 1==1不成立 {%endif %}\n" "{% if !x %} x為空 {% else %} x不為空 {%endif %}\n" "{% if x==2 %} x==2 成立 {% endif %}\n" "{% if x+1!=2 %} x+1!=2 成立 {% endif %}\n" "{% if x<3 %} x<3 成立 {% endif %}\n" "{% if x>1 %} x>1 成立 {% endif %}\n" "{% if str==\"abcd\" %} str為abcd {% endif %}\n" "{% if 1 %} 常量表達式1 {% endif %}\n" "{% if 0 %} 常量表達式0,此處不會輸出 {%endif%}", 1); JSONObject obj={ {"x", 2}, {"str", "abcd"} }; cout << html.setValue(obj).render() << endl; /*運行后輸出 1==1 成立 x不為空 x==2 成立 x+1!=2 成立 x<3 成立 x>1 成立 str為abcd 常量表達式1 */
6.for與if嵌套使用
HtmlTemplate html("{%for x in list%}" "{%if x %}" "{% for y in list2%}" "{{x}} * {{y}}={{ x*y }}\n" "{% endfor %}" "{% else %}" "x的值為空\n" "{%endif%}" "{% endfor%}", 1); JSONObject obj=OBJECT( KEYVALUE("list", LIST(1,2,3,4,5)), KEYVALUE("list2", LIST(1,2,3)), ); cout << html.setValue(obj).render() << endl << endl; /*運行后輸出 1 * 1=1 1 * 2=2 1 * 3=3 2 * 1=2 2 * 2=4 2 * 3=6 3 * 1=3 3 * 2=6 3 * 3=9 4 * 1=4 4 * 2=8 4 * 3=12 5 * 1=5 5 * 2=10 5 * 3=15 */
7.模版文件作為輸出
HtmlTemplate html("tmpl.html"); JSONObject context=OBJECT( ... ); FILE* f=fopen("output.html", "w"); // 寫入到文件中 string&& str=html.setValue(context).render(); fwrite(str.c_str(), 1, str.size(), f); fclose(f); /*運行后,代開當前目錄的tmpl.html文件作為輸入,輸出文件為output.html*/ /*如果tmpl.html不存在則拋出異常*/
8. 異常處理
HtmlTemplate html("{% if 1 %} xxx ", 1); // 不傳入context try { cout << html.render() << endl; } catch(exception& e) { cerr << e.what() << endl; } cout << endl;
運行后終端上打印如下,
會提示異常的類名,異常文件所在位置,代碼行數,以及一些錯誤的信息。
討論
1. 實現一個簡單的表達式計算器用什么方法比較好?(例如 {{ 2.3*3+4/5*x }} 這類表達式)
我分享一下我自己的方法,有什么更好的方法一起討論一下。
double cJinja::HtmlTemplate::calculator(vector<any>& number, vector<char>& op) { // 例如下表達式會成為 // 1 - 2 - 3 + 2 *3 * 4 - 4*5 // vector<char> op={ '-', '-', '+', '*', '*', '-', '*' }; // vector<any> number={ 1, 2, 3, 2, 3, 4, 4, 5 }; if (number.size() !=op.size() + 1) throwException(TemplateParseException, "運算符號數和操作數不匹配"); /* 定義計算器的內部函數 */ auto calc=[](any& var1, double var2, char op) -> double{ // var2 + var1 // var2 * var1 // var2 - var1 // var2 / var1 // 注意順序 #define CALC(op2) \ if(#op2[0]==op) { \ if (var1.type()==typeid(int)) \ return var2 op2 static_cast<double>(any_cast<int>(var1)); \ else if (var1.type()==typeid(float)) \ return var2 op2 static_cast<double>(any_cast<float>(var1)) ; \ else if (var1.type()==typeid(double)) \ return var2 op2 static_cast<double>(any_cast<double>(var1)) ; \ } CALC(+); CALC(-); CALC(*); CALC(/); throwException(TemplateParseException, "不允許對空指針進行運算"); #undef CALC }; vector<double> num_stack; // 計算中間結果存儲棧 num_stack.push_back(calc(number[0], 0, '+')); // 獲取值 number[i+1] + 0 (加法運算零元為0,乘法運算零元為1) /* 計算 * / 法*/ for (size_t i=0; i < op.size(); i++) { if (op[i]=='+' || op[i]=='-') { num_stack.push_back(calc(number[i + 1], 0, '+')); // number[i+1] + 0 } else if (op[i]=='*' || op[i]=='/') { double var1=num_stack.back(); num_stack.pop_back(); num_stack.push_back(calc(number[i + 1], var1, op[i])); // var1/number[i+1] 或者是 var1/number[i+1] } else throwException(TemplateParseException, str_format("非法操作符 %d", op[i])); } /* 計算 + - 法*/ double result=num_stack[0]; size_t i=1; for (auto& ch : op) { if (ch=='+') { result +=num_stack[i++]; } else if(ch=='-') { result -=num_stack[i++]; } } return result; }
2. 拋出異常包含更多的信息
我定義了一個throwException宏,如下
#define throwException(Exception, ...) { \ std::cerr << "[" << #Exception << "] : FILE: " << string(__FILE__).substr(string(__FILE__).find_last_of('/') + 1) << " LINE: " << __LINE__ << " FUNCTION: " <<__FUNCTION__ << std::endl; \ throw Exception(__VA_ARGS__); \ }
其中__FILE__ 為文件名,__LINE__ 為當前代碼行數,這些都是C中的內置宏,__VA_ARGS__是可變參數,對應于宏函數參數中的....
inja2提供了一系列的控制語句,用于在模板中實現邏輯控制。以下是Jinja2中常用的控制語句:
條件語句:
{% if %}: 檢查條件是否為真,如果為真則執行大括號內的代碼。
{% elif %}: 如果前面的條件為假,則檢查這個條件。
{% else %}: 如果前面的所有條件都為假,則執行這個代碼塊。
{% endif %}: 結束if語句塊。
示例:
{% if user %}
歡迎, {{ user.name }}
{% elif guest %}
你好, 匿名用戶
{% else %}
請登錄
{% endif %}
循環語句:
{% for %}: 遍歷序列或字典。
{% endfor %}: 結束for循環。
示例:
{% for item in items %}
{% endfor %}
宏(Macros):
{% macro %}: 定義一個可重用的代碼塊,類似于函數。
{% endmacro %}: 結束宏定義。
示例:
{% macro render_item(item) %}
{% endmacro %}
{% for item in items %}
{{ render_item(item) }}
{% endfor %}
包含(Include):
{% include %}: 包含另一個模板文件的內容。
示例:
{% include 'partials/header.html' %}
標簽控制:
{% set %}: 設置一個變量。
{% del %}: 刪除一個變量。
示例:
{% set my_var='Hello World' %}
{{ my_var }}
{% del my_var %}
{# 此時my_var已被刪除 #}
循環控制:
{% break %}: 跳出當前循環。
{% continue %}: 跳過當前循環的剩余部分,繼續下一次迭代。
示例:
{% for item in items %}
{% if item=='特定值' %}
{% continue %}
{% endif %}
{% endfor %}
注釋:
{# #}: 在模板中添加注釋,注釋內容不會被渲染到最終的HTML中。
示例:
{# 這是一條注釋 #}
這些控制語句使得Jinja2模板能夠靈活地實現復雜的邏輯控制,從而在不改變后端代碼的情況下,直接在模板層面進行數據展示的控制。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。