久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

詳解模板渲染引擎 jinja2

 古明地覺O_o 2022-12-08 發(fā)布于北京

楔子

jinja2 應(yīng)該是 Python 里面最著名的模板渲染引擎了,,并且提到 jinja2,很多人會立刻想到 flask,,因為 flask 在渲染模板的時候用的就是它,。

但 jinja2 不和 flask 綁定,,它是獨立于 flask 存在的,這就使得 jinja2 可以應(yīng)用在很多地方,。像一些能夠生成 html 的繪圖框架、分析工具,,內(nèi)部很多都使用了 jinja2,,比如 pandas, pyecharts 等等。

那么下面就來單獨地介紹一下 jinja2 的模板渲染語法,。


模板傳參

先來看看最簡單的字符串替換:

import jinja2

# 將想要替換的內(nèi)容使用 {{}} 包起來
string = "姓名: {{name}}, 年齡: {{age}}"

# 得到模板對象
temp = jinja2.Template(string)

# 調(diào)用 render 方法進行渲染
# 返回渲染之后的字符串
render_string = temp.render(name="古明地覺", age=16)
print(render_string)
"""
姓名: 古明地覺, 年齡: 16
"""

用法相當(dāng)簡單,,并且和字符串格式化非常類似:

string = "姓名: {name}, 年齡: {age}"

render_string = string.format(name="古明地覺", age=16)
print(render_string)
"""
姓名: 古明地覺, 年齡: 16
"""

兩者非常類似,只不過字符串格式化使用的是一對大括號,,而 jinja2 使用的是兩對大括號,。但如果只是簡單的字符串替換,那么使用 jinja2 就有點大材小用了,,因為 jinja2 支持的功能遠不止這些,。

import jinja2

string = "姓名: {{info['name']}}, 年齡: {{info['age']}}"

temp = jinja2.Template(string)
render_string = temp.render(
    info={"name""古明地覺""age"16})
print(render_string)
"""
姓名: 古明地覺, 年齡: 16
"""

可以看到 jinja2 不僅僅支持字符串替換,在替換的時候還可以做一些額外的操作,,并且這些操作不止局限于字典(以及其它對象)的取值,,算術(shù)運算、函數(shù)調(diào)用也是支持的,。

import jinja2

string = """{{numbers * 3}}
{{tuple1 + tuple2}}
{{np.array([1, 2, 3])}}
"""


temp = jinja2.Template(string)
render_string = temp.render(
    numbers=[123],
    tuple1=("a""b"),
    tuple2=("c""d"),
    np=__import__("numpy")
)
print(render_string)
"""
[1, 2, 3, 1, 2, 3, 1, 2, 3]
('a', 'b', 'c', 'd')
[1 2 3]
"""

還是很強大的,,但有兩個注意的點,首先我們不能定義空的花括號,。

import jinja2

string = "---{{}}---"

try:
    temp = jinja2.Template(string)
except jinja2.TemplateSyntaxError as e:
    print(e)
"""
Expected an expression, got 'end of print statement'
"""

{{}} 內(nèi)部必須要指定相應(yīng)的參數(shù),否則報錯,。但如果指定了參數(shù),,在渲染的時候不傳,會怎么樣呢,?

import jinja2

string = "---{{name}}---"

temp = jinja2.Template(string)
render_string = temp.render()
print(render_string)
"""
------
"""

我們看到不傳也不會報錯,,在渲染的時候會直接丟棄,按照來處理,??扇绻麉?shù)進行了某種操作,那么就必須要給參數(shù)傳值了,,舉個例子,。

import jinja2

# 對參數(shù) name 進行了操作, 所以必須傳值
string = "---{{name.upper()}}---"

temp = jinja2.Template(string)
render_string = temp.render(name="satori")
print(render_string)
"""
---SATORI---
"""


try:
    temp.render()
except jinja2.UndefinedError as e:
    print(e)
"""
'name' is undefined
"""
 

關(guān)于模板傳參就說到這里,還是很簡單的,。


過濾器

過濾器的概念應(yīng)該不需要多說,,在 jinja2 中通過 | 來實現(xiàn)過濾器,。

例如:{{name | length}},會返回 name 的長度,。過濾器相當(dāng)于是一個函數(shù),,參數(shù)接收到的值會傳到過濾器中,然后過濾器根據(jù)自己的功能再返回相應(yīng)的值,,最后將結(jié)果渲染到頁面中,。

jinja2 內(nèi)置了很多的過濾器,下面介紹一些常用的:

import jinja2

string = """{{array}} 的長度 -> {{array|length}}
{{array}} 的總和 -> {{array|sum}}
{{array}} 的第一個元素 -> {{array|first}}
{{array}} 的最后一個元素 -> {{array|last}}
{{array}} 使用 {{sep}} 拼接的字符串 -> {{array|join(sep)}}
{{count}} 轉(zhuǎn)成整數(shù) -> {{count|int}}
{{count}} 轉(zhuǎn)成浮點數(shù) -> {{count|float}}
{{count}} 轉(zhuǎn)成字符串 -> {{count|string}}
{{count}} 的絕對值 -> {{count|abs}}
{{name}} 轉(zhuǎn)成小寫 -> {{name|lower}}
{{name}} 轉(zhuǎn)成大寫 -> {{name|upper}}
{{name}} 的 'i' 替換成 'I' -> {{name|replace('i', "I")}}
{{name}} 反向取值 -> {{name|reverse}}

字符串過長, 使用省略號表示
最多顯示 10 位 -> {{long_text|truncate(length=10)}}
"""


temp = jinja2.Template(string)
render_string = temp.render(
    array=[12345],
    sep='_',
    count=-666.66,
    name="Koishi",
    long_text="a" * 100
)
print(render_string)
"""
[1, 2, 3, 4, 5] 的長度 -> 5
[1, 2, 3, 4, 5] 的總和 -> 15
[1, 2, 3, 4, 5] 的第一個元素 -> 1
[1, 2, 3, 4, 5] 的最后一個元素 -> 5
[1, 2, 3, 4, 5] 使用 _ 拼接的字符串 -> 1_2_3_4_5
-666.66 轉(zhuǎn)成整數(shù) -> -666
-666.66 轉(zhuǎn)成浮點數(shù) -> -666.66
-666.66 轉(zhuǎn)成字符串 -> -666.66
-666.66 的絕對值 -> 666.66
Koishi 轉(zhuǎn)成小寫 -> koishi
Koishi 轉(zhuǎn)成大寫 -> KOISHI
Koishi 的 'i' 替換成 'I' -> KoIshI
Koishi 反向取值 -> ihsioK

字符串過長, 使用省略號表示
最多顯示 10 位 -> aaaaaaa...
"""

以上就是 jinja2 內(nèi)置的一些常用的過濾器,,然后還有一個特殊的過濾器 default,。前面說了,如果不給 {{}} 里面的參數(shù)傳值的話,,那么默認會不顯示,,也不報錯。但如果我們希望在不傳遞的時候,,使用默認值該怎么辦呢,?

import jinja2

string = "{{sign|default('這個人很懶,什么也沒留下')}}"

temp = jinja2.Template(string)
render_string = temp.render(
    sign="不裝了,,攤牌了,,我就是高級特工氚疝鉀"
)
print(render_string)
"""
不裝了,攤牌了,,我就是高級特工氚疝鉀
"""


# 不指定,,會使用默認值
render_string = temp.render()
print(render_string)
"""
這個人很懶,什么也沒留下
"""

default 還有一個參數(shù) boolean,,因為 default 是否執(zhí)行,,不在于傳的是什么值,而在于有沒有傳值,。只要傳了,,就不會顯示 default 里面的內(nèi)容。

那如果我想當(dāng)傳入空字符串,,空字典等等,,在 Python 中為假的值,還是等價于沒傳值,,繼續(xù)顯示 default 里的默認值,,該怎么辦呢?

很簡單,,可以將參數(shù) boolean 指定為 True,,表示只有當(dāng)布爾值為真時,才使用我們傳遞的值,。否則,,仍顯示 default 里的默認值,。

import jinja2

string = "{{sign1|default('這個人很懶,什么也沒留下')}}\n" \
    "{{sign2|default('這個人很懶,,什么也沒留下', boolean=True)}}"

temp = jinja2.Template(string)
# sign1 和 sign2 接收的都是空字典,,布爾值為假
render_string = temp.render(
    sign1={},
    sign2={}
)
# 對于 sign1 而言,只要傳值了,,就會顯示我們傳的值
# 對于 sign2 而言,,不僅要求傳值,還要求布爾值為真,,否則還是會使用默認值
print(render_string)
"""
{}
這個人很懶,,什么也沒留下
"""

可以看到 jinja2 內(nèi)置了很多的過濾器,但如果我們的業(yè)務(wù)場景比較特殊,,jinja2 內(nèi)置的過濾器滿足不了,,該怎么辦呢?沒關(guān)系,,jinja2 還支持我們自定制過濾器,。

自定制過濾器

過濾器本質(zhì)上就是個函數(shù),因此我們只需要寫個函數(shù),,定義相應(yīng)的邏輯,,然后注冊到 jinja2 過濾器當(dāng)中即可。下面我們手動實現(xiàn)一個 replace 過濾器,。

import jinja2

string = "{{name|my_replace('i', 'I')}}"

# 定義過濾器對應(yīng)的函數(shù)
# jinja2 在渲染的時候,,就會執(zhí)行這里的 my_replace 函數(shù)
def my_replace(s, old, new):
    """
    需要一提的是,過濾器里面接收了兩個參數(shù)
    但函數(shù)要定義三個參數(shù),,因為在調(diào)用的時候 name 也會傳過來
    所以像 {{name|length}} 這種,,它和 {{name|length()}} 是等價的
    函數(shù)至少要能接收一個參數(shù)
    """

    return s.replace(old, new)

# 此時函數(shù)就定義好了,但它目前和過濾器還沒有什么關(guān)系,,只是名字一樣而已
# 我們還需要將過濾器和函數(shù)注冊到 jinja2 當(dāng)中

# 這里調(diào)用了一個新的類 Environment
# jinja2.Template 本質(zhì)上也是調(diào)用了 Environment
env = jinja2.Environment()
# 將過濾器和函數(shù)綁定起來,,注冊到 jinja2 當(dāng)中
# 并且過濾器的名字和函數(shù)名可以不一樣
env.filters["my_replace"] = my_replace
# 返回 Template 對象
temp = env.from_string(string)
# 調(diào)用 render 方法渲染
render_string = temp.render(name="koishi")
print(render_string) 
"""
koIshI
"""

Environment 是 jinja2 的核心組件,包含了配置,、過濾器、全局環(huán)境等一系列重要的共享變量,。如果我們想自定制過濾器的話,,那么必須手動實例化這個對象,然后注冊進去,。通過調(diào)用它的 from_string 方法,,得到 Template 對象,這樣在渲染的時候就能找到我們自定制的過濾器了,。

事實上,,我們之前在實例化 Template 對象時,,底層也是這么做的。

因此我們后續(xù)就使用 Environment 這個類,,當(dāng)然 Template 也是可以的,。


邏輯語句

jinja2 還支持 if、for 等邏輯語句,,來看一下,。

import jinja2

# 如果是接收具體的值,那么使用 {{}}
# 但 if,、for 等邏輯語句,,則需要寫在 {% %} 里面
string = """
{% if info['math'] >= 90 %}
    {{info['name']}} 的數(shù)學(xué)成績?yōu)?nbsp;A
{% elif info['math'] >= 80 %}
    {{info['name']}} 的數(shù)學(xué)成績?yōu)?nbsp;B
{% elif info['math'] >= 60 %}
    {{info['name']}} 的數(shù)學(xué)成績?yōu)?nbsp;C
{% else %}
    {{info['name']}} 的數(shù)學(xué)成績?yōu)?nbsp;D
{% endif %}"""

# 和 Python 的 if 語句類似
# 但是結(jié)尾要有一個 {%endif %}

env = jinja2.Environment()
temp = env.from_string(string)
render_string = temp.render(
    info={"math"85"name""古明地覺"}
)
print(render_string)
"""
    古明地覺 的數(shù)學(xué)成績?yōu)?nbsp;B
"""


render_string = temp.render(
    info={"math"9"name""琪露諾"}
)
print(render_string)
"""
    琪露諾 的數(shù)學(xué)成績?yōu)?nbsp;D
"""

并且 if 語句還可以多重嵌套,都是可以的,。

然后是 for 語句:

import jinja2

# 和 Python for 循環(huán)等價
# 但不要忘記結(jié)尾的 {% endfor %}
string = """
{% for girl in girls %}
    姓名: {{girl['name']}}, 地址: {{girl['address']}}
{% endfor %}
"""


env = jinja2.Environment()
temp = env.from_string(string)
render_string = temp.render(
    girls=[{"name""古明地覺""address""地靈殿"},
           {"name""琪露諾""address""霧之湖"},
           {"name""魔理沙""address""魔法森林"}]
)
print(render_string)
"""
    姓名: 古明地覺, 地址: 地靈殿

    姓名: 琪露諾, 地址: 霧之湖

    姓名: 魔理沙, 地址: 魔法森林
"""

所以 {% for girl in girls %} 這段邏輯和 Python 是等價的,,先確定 girls 的值,然后遍歷,。因此在里面也可以使用過濾器,,比如 {% for girl in girls|reverse %} 便可實現(xiàn)對 girls 的反向遍歷。

如果在遍歷的時候,,還想獲取索引呢,?

import jinja2

string = """
{% for name, address in girls.items()|reverse %}
    --------------------------------
    姓名: {{name}}, 地址: {{address}}
    索引(從1開始): {{loop.index}}
    索引(從0開始): {{loop.index0}}
    是否是第一次迭代: {{loop.first}}
    是否是最后一次迭代: {{loop.last}}
    序列的長度: {{loop.length}}
    --------------------------------
{% endfor %}
"""


env = jinja2.Environment()
temp = env.from_string(string)
render_string = temp.render(girls={"古明地覺""地靈殿",
                                   "琪露諾""霧之湖",
                                   "魔理沙""魔法森林"})
print(render_string)
"""
    --------------------------------
    姓名: 魔理沙, 地址: 魔法森林
    索引(從1開始): 1
    索引(從0開始): 0
    是否是第一次迭代: True
    是否是最后一次迭代: False
    序列的長度: 3
    --------------------------------

    --------------------------------
    姓名: 琪露諾, 地址: 霧之湖
    索引(從1開始): 2
    索引(從0開始): 1
    是否是第一次迭代: False
    是否是最后一次迭代: False
    序列的長度: 3
    --------------------------------

    --------------------------------
    姓名: 古明地覺, 地址: 地靈殿
    索引(從1開始): 3
    索引(從0開始): 2
    是否是第一次迭代: False
    是否是最后一次迭代: True
    序列的長度: 3
    --------------------------------

"""

可以看到 jinja2 還是很強大的,因為它不僅僅是簡單的替換,,而是一個模板渲染引擎,,并且內(nèi)部還涉及到編譯原理。jinja2 也是先通過 lexing 進行分詞,,然后 parser 解析成 AST,,再基于 optimizer 優(yōu)化AST,最后在當(dāng)前的環(huán)境中執(zhí)行,。

所以 jinja2 一般用于渲染 HTML 頁面等大型文本內(nèi)容,,那么問題來了,如果有一個 HTML 文本,,jinja2 要如何加載它呢,?

from jinja2 import Environment, FileSystemLoader
env = Environment(
    # 指定一個加載器,里面?zhèn)魅胨阉髀窂?/span>
    loader=FileSystemLoader(".")
)

# 在指定的路徑中查找文件并打開,,同樣會返回 Template 對象
temp = env.get_template("login.html")
# 注意:此處不能手動調(diào)用 Template
# 如果是手動調(diào)用 Template("login.html") 的話
# 那么 "login.html" 會被當(dāng)成是普通的字符串

print(temp.render())
"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>歡迎來到古明地覺的編程教室</h3>
</body>
</html>
"""

當(dāng)然啦,,不管是以普通字符串的形式,還是以文本文件的形式,,jinja2 的語法都是不變的,。


宏的定義和使用

宏,說得通俗一點,,就類似于函數(shù),。我們給一系列操作進行一個封裝,,再給一個名字,然后調(diào)用宏名的時候,,就會執(zhí)行預(yù)定義好的一系列操作,。

from jinja2 import Environment
env = Environment()

# 通過 {% macro %} 可以定義一個宏
# 這里的宏叫 input,當(dāng)然叫什么無所謂
string = """
{% macro input(name, value="", type="text") %}
<input type="{{type}}" name="{{name}}" value="{{value}}"/>
{% endmacro %}

{{input("satori","東方地靈殿")}}
{{input("marisa","魔法森林")}}
{{input("", "提交", "submit")}}
"""

temp = env.from_string(string)
print(temp.render().strip())
"""
<input type="text" name="satori" value="東方地靈殿"/>


<input type="text" name="marisa" value="魔法森林"/>


<input type="submit" name="" value="提交"/>
"""

此外宏也是可以導(dǎo)入的,,既然涉及到導(dǎo)入,,那么就需要寫在文件里面了。而之所以要有宏的導(dǎo)入,,也是為了分文件編程,,這樣看起來更加清晰。

marco.html

{% macro input(name, value="", type="text") %}
<input type="{{type}}" name="{{name}}" value="{{value}}"/>
{% endmacro %}

這樣我們就把宏單獨定義在一個文件里面,,先通過 import "宏文件的路徑" as xxx 來導(dǎo)入宏,,然后再通過 xxx.宏名 調(diào)用即可。注意這里必須要起名字,,也就是必須要 as,。或者 from "宏文件的路徑" import 宏名 [as xxx],,這里起別名則是可選的,。

login.html

{% import "macro.html" as macro  %}

{{macro.input("satori","東方地靈殿")}}
{{macro.input("mashiro","櫻花莊的寵物女孩")}}
{{macro.input("""提交""submit")}}

然后 Python 代碼和之前類似,直接加載 login.html 然后渲染即可,。


include 的使用

include 的使用就很簡單了,,相當(dāng)于 Ctrl + C 和 Ctrl + V。

1.txt

古明地覺,,一個幽靈也為之懼怕的少女
但當(dāng)你推開地靈殿的大門,,卻發(fā)現(xiàn)...

2.txt

{% include "1.txt" %}
她居然在調(diào)戲她的妹妹

我們使用的文件一直都是 html 文件,但 txt 文件也是可以的,。

from jinja2 import Environment, FileSystemLoader
env = Environment(
    loader=FileSystemLoader(".")
)

temp = env.get_template("2.txt")
print(temp.render())
"""
古明地覺,,一個幽靈也為之懼怕的少女
但當(dāng)你推開地靈殿的大門,卻發(fā)現(xiàn)...
她居然在調(diào)戲她的妹妹
"""

所以 include 就相當(dāng)于將文件里的內(nèi)容復(fù)制粘貼過來,。


通過 set 和 with 語句定義變量

在模板中,,我們還可以定義一個變量,然后在其它的地方用,。

from jinja2 import Environment
env = Environment()

string = """
{% set username="satori" %}
<h2>{{username}}</h2>

{% with username="koishi" %}
<h2>{{username}}</h2>
{% endwith %}

<h2>{{username}}</h2>
"""

temp = env.from_string(string)
# 使用 set 設(shè)置變量,,在全局都可以使用
# 使用 with 設(shè)置變量,那么變量只會在 with 語句塊內(nèi)生效
# 所以結(jié)尾才要有 {% endwith %} 構(gòu)成一個語句塊
print(temp.render().strip())
"""
<h2>satori</h2>


<h2>koishi</h2>


<h2>satori</h2>
"""

此外 with 還有另一種寫法:

{% with %}
{% set username = "koishi" %}
{% endwith %}

這樣寫也是沒問題的,,因為 set 在 with 里面,所以變量只會在 with 語句塊內(nèi)生效,。


模板繼承

對于很多網(wǎng)站的頁面來說,,它的四周有很多內(nèi)容都是不變的,,如果每來一個頁面都要寫一遍的話,會很麻煩,。因此我們可以將不變的部分先寫好,,在變的部分留一個坑,這就是父模板,。然后子模板繼承的時候,,會將父模板不變的部分繼承過來,然后將變的部分,,也就是父模板中挖的坑填好,。總結(jié)一下就是:父模板挖坑,,子模板填坑,。

base.html

<p>古明地覺:我的家在哪呢?</p>
{% block 古明地覺 %}
{% endblock %}

<p>魔理沙:我的家在哪呢,?</p>
{% block 魔理沙 %}
{% endblock %}

<p>芙蘭朵露:我的家在哪呢,?</p>
{% block 芙蘭朵露 %}
{% endblock %}

<p>找不到家的話,就跟我走吧</p>

child.html

{% extends "base.html" %}

{% block 古明地覺 %}
<p>你的家在地靈殿</p>
{% endblock %}

{% block 魔理沙 %}
<p>你的家在魔法森林</p>
{% endblock %}

{% block 芙蘭朵露 %}
<p>你的家在紅魔館</p>
{% endblock %}

在 base.html 里面通過 {% block %} 挖坑,,子模板繼承過來之后再填坑,。以下是 child.html 渲染之后的內(nèi)容:

執(zhí)行結(jié)果沒有問題,并且父模板在挖坑的時候,,如果里面有內(nèi)容,,那么子模板在繼承之后會自動清除,但也可以使用 {{super()}} 保留下來,。

base.html

<p>古明地覺:我的家在哪呢,?</p>
{% block 古明地覺 %}
<p>父模板挖坑時填的內(nèi)容</p>
{% endblock %}

<p>魔理沙:我的家在哪呢?</p>
{% block 魔理沙 %}
{% endblock %}

child.html

{% extends "base.html" %}

{% block 古明地覺 %}
<p>子模板繼承的時候,,默認會清空父模板的內(nèi)容</p>
<p>但可以通過 super 保留下來,,以下是父模板寫入的內(nèi)容</p>
{{super()}}
{% endblock %}


{% block 魔理沙 %}
<p>通過 self.塊名() 可以在一個塊內(nèi)引用其它塊的內(nèi)容</p>
<p>以下是 古明地覺 塊里的內(nèi)容</p>
{{self.古明地覺()}}
{% endblock %}

以下是 child.html 渲染之后的內(nèi)容:

可以看到,在引用其它塊的內(nèi)容時,,會把其它塊繼承的父模板的內(nèi)容一塊引用過來,。因為一旦繼承,那么就變成自己的了,。

最后 {% extend "xxx.html"  %} 要放在最上面,,不然容易出問題,在 Django 會直接報錯,。另外子模板中的代碼一定要放在 block 語句塊內(nèi),,如果放在了外面,jinja2 是不會渲染的。

以上就是 jinja2 相關(guān)的內(nèi)容,,當(dāng)我們希望按照指定規(guī)則生成文件時,,不妨讓 jinja2 來替你完成任務(wù)吧。

作為一款模板渲染引擎,,jinja2 無疑是最出名的,,但其實 jinja2 是借鑒了 Django 的模板渲染引擎。只不過 Django 的引擎和 Django 本身是強耦合的,,而 jinja2 是獨立存在的,,這也使得它可以應(yīng)用在除 web 框架之外的很多地方。

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多