整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          5分鐘學會用Python Jinja2模板引擎渲染HTML網頁

          深入Python Web開發的過程中,HTML模板渲染是構建動態Web應用的重要環節。今天將詳細探討如何使用Python中最流行的模板引擎之一——Jinja2來進行高效且靈活的模板渲染。通過具體的代碼示例,將了解如何結合Flask框架與Jinja2實現數據與視圖的完美分離。

          Jinja2簡介

          Jinja2是一個強大的現代模板引擎,設計用于Python web開發項目,它支持變量替換、控制結構、過濾器和宏等豐富的功能。Flask框架默認集成并推薦使用Jinja2進行模板渲染,使其成為Python Web開發者手中不可或缺的工具。

          安裝與配置Jinja2(適用于未安裝Flask環境)

          如果已經安裝過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模板,并將定義好的變量替換到相應的位置。

          Jinja2高級特性

          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頁面。應該算是后端渲染不可缺少的組成部分。

          二、功能介紹

          通過使用學習 tornadobottle 的模板語言,我也效仿著實現可以獨立使用的模板渲染的代碼模塊,模板語法來自 tornadobottle 的語法。可以用來做一些簡單的事情 網頁渲染郵件內容生成 等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的 forwhile 。與條件控制一樣也需要放在 {% %} 內部,并且結束處需要額外增加 {% 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+ 上使用需要對 strbytes 進行一些處理即可,由于沒有引用任何其他模塊,可以很好地獨立使用。

            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


          主站蜘蛛池模板: 国产爆乳无码一区二区麻豆| 国产日韩一区二区三区在线播放| 精品人妻一区二区三区四区在线| 亚洲乱色熟女一区二区三区蜜臀 | 亚洲AV无码一区二三区| 国产Av一区二区精品久久| 亚洲a∨无码一区二区| 国产精品高清一区二区三区不卡| 亚拍精品一区二区三区| 国内自拍视频一区二区三区 | 国产免费播放一区二区| 国产一在线精品一区在线观看| 真实国产乱子伦精品一区二区三区 | 无码国产精品一区二区免费模式 | 国产精品一区在线观看你懂的| 国产成人综合精品一区| 国产成人无码AV一区二区 | 国产探花在线精品一区二区| 国产内射在线激情一区| 3d动漫精品啪啪一区二区中文| 精品一区二区三区在线播放视频| 多人伦精品一区二区三区视频| 秋霞鲁丝片一区二区三区| 国产福利一区二区在线视频 | 午夜影院一区二区| 国产aⅴ精品一区二区三区久久| 无码精品前田一区二区| 亚洲熟女一区二区三区| 精品一区高潮喷吹在线播放| 在线|一区二区三区四区| 福利视频一区二区牛牛| 国内国外日产一区二区| 日本免费一区二区三区四区五六区| 国产美女一区二区三区| 一区视频免费观看| 精品无码AV一区二区三区不卡| 在线|一区二区三区| 自慰无码一区二区三区| 中文字幕一区日韩精品| 亚洲男人的天堂一区二区| 午夜天堂一区人妻|