深入Python Web開發的過程中,HTML模板渲染是構建動態Web應用的重要環節。今天將詳細探討如何使用Python中最流行的模板引擎之一——Jinja2來進行高效且靈活的模板渲染。通過具體的代碼示例,將了解如何結合Flask框架與Jinja2實現數據與視圖的完美分離。
Jinja2是一個強大的現代模板引擎,設計用于Python web開發項目,它支持變量替換、控制結構、過濾器和宏等豐富的功能。Flask框架默認集成并推薦使用Jinja2進行模板渲染,使其成為Python Web開發者手中不可或缺的工具。
如果已經安裝過Flask,Jinja2通常已隨Flask一同安裝。若需要單獨安裝:
pip install Jinja2
1.創建模板文件
在項目中創建一個名為templates的文件夾,并在其內放置我們的HTML模板文件,例如index.html:
<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Html渲染示例</title>
</head>
<body>
<h1>{{ title }}</h1>
<p>Welcome to {{ site_name }}!</p>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>
在這個模板中,我們用{{ variable }}表示變量占位符,{% %}表示控制結構
2.Flask中加載和渲染模板
在Flask應用中,我們需要導入render_template函數來加載并渲染模板:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
# 定義傳遞給模板的數據
title = '主頁'
site_name = 'Html模板渲染示例'
items = ['Item 1', 'Item 2', 'Item 3']
# 渲染模板并將數據傳遞給模板
return render_template('index.html', title=title, site_name=site_name, items=items)
if __name__ == '__main__':
app.run(debug=True)
當用戶訪問主頁時,Flask會調用home函數,其中render_template函數會查找templates目錄下的index.html模板,并將定義好的變量替換到相應的位置。
1. 控制結構
除了簡單的循環外,Jinja2還支持條件判斷和其他邏輯操作:
<!-- 在模板中添加條件判斷 -->
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
2. 過濾器
Jinja2內置了眾多過濾器,可以對變量進行處理,如格式化日期、轉換大小寫等:
<!-- 使用過濾器格式化日期 -->
<p>The date is: {{ current_date|date("Y-m-d") }}</p>
3. 宏
宏允許復用或封裝常見的HTML片段,提高代碼可讀性和維護性:
{# 在一個單獨的macros.html模板中定義宏 #}
{% macro render_item(item) %}
<div class="item">
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
</div>
{% endmacro %}
然后在其他模板中引入并使用該宏:
{% from 'macros.html' import render_item %}
<ul>
{% for item in items %}
{{ render_item(item) }}
{% endfor %}
</ul>
通過本文,我們已經深入了解了Jinja2模板引擎的基礎使用方法以及其高級特性。掌握好Jinja2能夠顯著提升你的Web應用開發效率,實現更復雜、更美觀的動態頁面布局。
關注我,手把手帶你快速入門Python Web編程!
模板語言由HTML代碼和邏輯控制代碼組成,此處 @PHP 。通過模板語言可以快速的生成預想的HTML頁面。應該算是后端渲染不可缺少的組成部分。
通過使用學習 tornado 、 bottle 的模板語言,我也效仿著實現可以獨立使用的模板渲染的代碼模塊,模板語法來自 tornado 和 bottle 的語法。可以用來做一些簡單的事情 網頁渲染 , 郵件內容生成 等HTML顯示方面。以下就是簡單的語法使用介紹。
1. 變量。使用 {{ }} 包裹起來,里面的變量為Python傳入。模板渲染時會將傳入的變量轉換成字符串并填入對應位置。
# 模板文件內容
<title>{{my_title}}</title>
<label>{{ session.name }}</label>
# py代碼調用 t_html 為上面的內容
Template(t_html).render(my_title="標題", session = some_obj)
2. 轉義。默認傳入的數據都會進行HTML轉義,可以使用 {% raw value %} 來將value的內容按原始字符串輸出。
# 模板文件內容
<p>{% raw value %} </p>
# Py調用內容
Template(t_html).render(my_title="<label>顯示標簽</label>")
3. 條件控制。支持Python的 if,elif,else 。條件代碼需要放在 {% %} 內部,并且在條件結束后需要額外增加 {% end %} ,用于標識條件控制語句塊范圍。
# 模板文件內容
{% if a > 1%}
<label>A大于1</label>
{% else %}
<label>A小于或等于1</label>
{% end %}
# py調用
Template(t_html).render(a=1)
4. 循環控制。支持Python的 for 和 while 。與條件控制一樣也需要放在 {% %} 內部,并且結束處需要額外增加 {% end %} ,用于標識循環控制語句塊的范圍。
# 模板文件內容
{% for i in range(10) %}
<label>當前序號:{{i+1}}</label>
{% end %}
# py調用
Template(t_html).render()
5. 變量聲明。如果需要在模板文件內聲明一個變量,方便使用時,可以通過 set 來實現。具體格式為 {% set v = xx %} 。通過 set 聲明的變量在整個模板文件中都可以使用,包括在條件控制和循環控制中作為條件判斷也可以。
# 模板文件內容
{% set a = 1 %}
<label>a的值:{{a}}</label>
這個模板語言模塊是在 Python2.7 上面開發使用的,如果要在 Python3+ 上使用需要對 str 和 bytes 進行一些處理即可,由于沒有引用任何其他模塊,可以很好地獨立使用。
1 # -*- coding:utf-8 -*-
2
3 """ 模板語言"""
4
5 # TOKEN相關的定義
6 TOKEN_S_BRACE = "{"
7 TOKEN_S_BLOCK = "%"
8 TOKEN_EXPRESSION_L = "{{"
9 TOKEN_EXPRESSION_R = "}}"
10 TOKEN_BLOCK_L = "{%"
11 TOKEN_BLOCK_R = "%}"
12 TOKEN_KEY_SET = "set"
13 TOKEN_KEY_RAW = "raw"
14 TOKEN_KEY_IF = "if"
15 TOKEN_KEY_ELIF = "elif"
16 TOKEN_KEY_ELSE = "else"
17 TOKEN_KEY_FOR = "for"
18 TOKEN_KEY_WHILE = "while"
19 TOKEN_KEY_END = "end"
20 TOKEN_KEY_BREAK = "break"
21 TOKEN_KEY_CONTINUE = "continue"
22 TOKEN_SPACE = " "
23 TOKEN_COLON = ":"
24 # Token標記 {{}} {% %}
25 TOKEN_FLAG_SET = {TOKEN_S_BRACE, TOKEN_S_BLOCK}
26 # 簡單的語句
27 TOKEN_KEY_SET_SIMPLE_EXPRESSION = {TOKEN_KEY_SET, TOKEN_KEY_RAW}
28 # 前置條件
29 TOKEN_KEY_PRE_CONDITION = {
30 # end 必須在if/elif/else/for/while 后面
31 TOKEN_KEY_END: {TOKEN_KEY_IF, TOKEN_KEY_ELIF, TOKEN_KEY_ELSE,
32 TOKEN_KEY_FOR, TOKEN_KEY_WHILE},
33 # elif 必須在if 后面
34 TOKEN_KEY_ELIF: {TOKEN_KEY_IF},
35 # else 必須在if/elif 后面
36 TOKEN_KEY_ELSE: {TOKEN_KEY_IF, TOKEN_KEY_ELIF, TOKEN_KEY_FOR, TOKEN_KEY_WHILE},
37 }
38 # 循環語句
39 TOKEN_KEY_LOOP = {TOKEN_KEY_WHILE, TOKEN_KEY_FOR}
40 # 循環的控制break continue
41 TOKEN_KEY_LOOP_CTRL = {TOKEN_KEY_BREAK, TOKEN_KEY_CONTINUE}
42
43 class ParseException(Exception):
44 pass
45
46 class TemplateCode(object):
47 def __init__(self):
48 self.codeTrees = {"parent": None, "nodes": []}
49 self.cursor = self.codeTrees
50 self.compiled_code = None
51
52 def create_code(self):
53 """創建一個代碼子塊"""
54 child_codes = {"parent": self.cursor, "nodes": []}
55 self.cursor["nodes"].append(child_codes)
56 self.cursor = child_codes
57
58 def close_code(self):
59 """ 關閉一個代碼子塊 """
60 assert self.cursor["parent"] is not None, "overflow"
61 self.cursor = self.cursor["parent"]
62
63 def append_text(self, text):
64 """ 添加文本 """
65 # 排除空行
66 self.cursor["nodes"].append("_add(%r)" % text)
67
68 def append_express(self, express, raw=False):
69 """ 表達式 """
70 if raw:
71 temp_exp = "_t_exp = _str_(%s)" % express
72 else:
73 temp_exp = "_t_exp = _esc_(%s)" % express
74 self.cursor["nodes"].append(temp_exp)
75 self.cursor["nodes"].append("_add(_t_exp)")
76
77 def append_statement(self, statement):
78 """ 語句 """
79 temp_statement = "%s" % statement
80 self.cursor["nodes"].append(temp_statement)
81
82 def reset(self):
83 self.codeTrees = {"parent": None, "nodes": []}
84 self.cursor = self.codeTrees
85 self.compiled_code = None
86
87 def build_code(self, filename):
88 temp_code_buff = []
89 self.write_buff_with_indent(temp_code_buff, "def _template_render():", 0)
90 self.write_buff_with_indent(temp_code_buff, "_codes = []", 4)
91 self.write_buff_with_indent(temp_code_buff, "_add = _codes.append", 4)
92 self.write_codes(temp_code_buff, self.codeTrees, 4)
93 self.write_buff_with_indent(temp_code_buff, "return ''.join(_codes)", 4)
94 temp_code = "".join(temp_code_buff)
95 self.compiled_code = compile(temp_code,filename, "exec", dont_inherit=True)
96
97 def write_codes(self, code_buff, codes, indent):
98 for node in codes.get("nodes", []):
99 if isinstance(node, dict):
100 self.write_codes(code_buff, node, indent+4)
101 else:
102 self.write_buff_with_indent(code_buff, node, indent)
103
104 def generate(self, **kwargs):
105 temp_namespace = {}
106 temp_namespace['_str_'] = self.to_utf8
107 temp_namespace['_esc_'] = self.to_safe_utf8
108 temp_namespace.update(kwargs)
109 exec(self.compiled_code, temp_namespace)
110 return temp_namespace['_template_render']()
111
112 @staticmethod
113 def write_buff_with_indent(code_buff, raw_str, indent):
114 """"""
115 temp = (" " * indent) + raw_str + "\n"
116 code_buff.append(temp)
117
118 @staticmethod
119 def to_utf8(raw_str):
120 """ 轉換 """
121 if isinstance(raw_str, str):
122 return raw_str
123 elif isinstance(raw_str, bytes):
124 return raw_str.decode()
125 return str(raw_str)
126
127 @staticmethod
128 def to_safe_utf8(raw_str):
129 """ 過濾html轉義 """
130 text = TemplateCode.to_utf8(raw_str)
131 return text.replace("&", "&").replace("<", "<").replace(">", ">")
132 class Template(object):
133 """模板類"""
134 def __init__(self, input_obj,filename="<string>", **namespace):
135 """模板初始化"""
136 self.namespace = {}
137 self.namespace.update(namespace)
138 # 將數據丟進去解析生成編譯代碼
139 self.lexer = TemplateLexer(input_obj, filename)
140
141 def render(self, **kwargs):
142 """渲染模板 """
143 temp_name_space = {}
144 temp_name_space.update(self.namespace)
145 temp_name_space.update(kwargs)
146 # 執行渲染
147 return self.lexer.render(**kwargs)
148
149 class TemplateLexer(object):
150 """模板語法分析器 """
151 def __init__(self, input_obb, filename="<string>"):
152 if hasattr(input_obb, "read"):
153 self.raw_string = input_obb.read()
154 else:
155 self.raw_string = input_obb
156 self.filename = filename
157 # 記錄當前的位置
158 self.pos = 0
159 # 記錄原始數據的總長度
160 self.raw_str_len = len(self.raw_string)
161 # 記錄解析的數據
162 self.code_data = TemplateCode()
163 # 開始解析
164 self.parse_template()
165
166 def match(self, keyword, pos=None):
167 return self.raw_string.find(keyword, pos if pos is not None else self.pos)
168
169 def cut(self, size=-1):
170 """剪取數據 size切割數據的大小,-1表示全部"""
171 if size == -1:
172 new_pos = self.raw_str_len
173 else:
174 new_pos = self.pos + size
175 s = self.raw_string[self.pos: new_pos]
176 self.pos = new_pos
177 return s
178
179 def remaining(self):
180 """獲取剩余大小 """
181 return self.raw_str_len - self.pos
182
183 def function_brace(self):
184 """ 獲取{{ / {% """
185 skip_index = self.pos
186 while True:
187 index = self.match(TOKEN_S_BRACE, skip_index) # {% {{
188 # 沒找到
189 if index == -1:
190 return None, -1
191 # 末尾
192 if index >= self.raw_str_len:
193 return None, -1
194 # 匹配類型
195 next_value = self.raw_string[index + 1:index + 2]
196 if next_value not in TOKEN_FLAG_SET:
197 skip_index = index + 1
198 # 說明不是關鍵類型
199 continue
200 brace = self.raw_string[index: index + 2]
201 return brace, index
202 return None, -1
203
204 def read_content_with_token(self, index, begin_token, end_token):
205 """
206 讀取匹配token的內容
207 """
208 end_index = self.match(end_token)
209 if end_index == -1:
210 return ParseException("{0} missing end token {1}".format(begin_token, end_token))
211 # 過濾 begin_token
212 self.pos = index + len(begin_token)
213 content = self.cut(end_index - self.pos)
214 # 去除末尾 end_token
215 self.cut(len(end_token))
216 return content
217
218 def add_simple_block_statement(self, operator, suffix):
219 if not suffix:
220 raise ParseException("{0} missing content".format(operator))
221 if operator == TOKEN_KEY_SET:
222 self.code_data.append_statement(suffix)
223 elif operator == TOKEN_KEY_RAW:
224 self.code_data.append_express(suffix, True)
225 else:
226 raise ParseException("{0} is undefined".format(operator))
227
228 def parse_template(self):
229 """解析模板 """
230 # TODO 檢查模板文件是否更改過,如果沒有則不需要重新解析
231 self.code_data.reset()
232 # 解析模板原文件
233 self.__parse()
234 # 生成編譯code
235 self.__compiled_code()
236
237 def render(self, **kwargs):
238 return self.code_data.generate(**kwargs)
239
240 def __parse(self, control_operator=None, in_loop=False):
241 """開始解析"""
242 while True:
243 if self.remaining() <= 0:
244 if control_operator or in_loop:
245 raise ParseException("%s missing {%% end %%}" % control_operator)
246 break
247 # 讀取 {{ {%
248 brace, index = self.function_brace()
249 # 說明沒有找到
250 if not brace:
251 text = self.cut(index)
252 self.code_data.append_text(text)
253 continue
254 else:
255 text = self.cut(index - self.pos)
256 if text:
257 self.code_data.append_text(text)
258
259 if brace == TOKEN_EXPRESSION_L:
260 content = self.read_content_with_token(index, TOKEN_EXPRESSION_L, TOKEN_EXPRESSION_R).strip()
261 if not content:
262 raise ParseException("Empty Express")
263 self.code_data.append_express(content)
264 continue
265 elif brace == TOKEN_BLOCK_L:
266 content = self.read_content_with_token(index, TOKEN_BLOCK_L, TOKEN_BLOCK_R).strip()
267 if not content:
268 raise ParseException("Empty block")
269
270 # 得到表達式 for x in x ; if x ; elif x ; else ; end ; set ; while x ;
271 operator, _, suffix = content.partition(TOKEN_SPACE)
272 if not operator:
273 raise ParseException("block missing operator")
274
275 suffix = suffix.strip()
276 # 簡單語句,set / raw
277 if operator in TOKEN_KEY_SET_SIMPLE_EXPRESSION:
278 self.add_simple_block_statement(operator, suffix)
279 elif operator in TOKEN_KEY_LOOP_CTRL:
280 if not in_loop:
281 raise ParseException("{0} must in loop block".format(operator))
282 self.code_data.append_statement(operator)
283 else:
284 # 控制語句 檢查匹配if 后面可以跟elif/else
285 pre_condition = TOKEN_KEY_PRE_CONDITION.get(operator, None)
286 if pre_condition:
287 # 里面就是elif/else/end
288 if control_operator not in pre_condition:
289 raise ParseException("{0} must behind with {1}".format(operator, pre_condition))
290 elif operator == TOKEN_KEY_END:
291 # 遇到{% end %}則結束
292 self.code_data.close_code()
293 return
294 else:
295 # 由于是依據if 進入 來計算elif ,因此elif與if是同級的
296 self.code_data.close_code()
297 self.code_data.append_statement(content + TOKEN_COLON)
298 self.code_data.create_code()
299 self.__parse(operator, in_loop or (operator in TOKEN_KEY_LOOP))
300 break
301 # 添加控制語句及內部語句體 if for while
302 self.code_data.append_statement(content + TOKEN_COLON)
303 self.code_data.create_code()
304 self.__parse(operator, in_loop or (operator in TOKEN_KEY_LOOP))
305 else:
306 raise ParseException("Unkown brace")
307 return
308
309 def __compiled_code(self):
310 """生成 編譯code """
311 self.code_data.build_code(self.filename)
312 if __name__ == "__main__":
313 t = Template("<html>{{hello}}</html>")
314 t.render(hello="你好"
原文鏈接:
http://www.cnblogs.com/jeffxun/p/15585073.html
*請認真填寫需求信息,我們會在24小時內與您取得聯系。