模板引擎Jinja2
一、简介及基本使用:
Flask使用Jinja2作为默认的模板引擎。Jinja2是一个功能强大且易于使用的模板引擎,它允许我们在HTML中嵌入Python代码,并通过将模板和数据进行渲染来生成动态内容。
实战之在Flask中使用Jinja2模板引擎:
-
在Flask应用程序的根目录下创建一个名为
templates
的文件夹,用于存放模板文件。 -
创建一个名为
index.html
的模板文件,在其中可以使用Jinja2语法来插入动态内容,即使用双花括号{``{ }}
来插入Jinja2表达式,这些表达式将在渲染模板时被替换为实际的值。html<!DOCTYPE html> <html> <head> <title>Flask Template Engine</title> </head> <body> <h1>Welcome to {{ title }}</h1> <p>{{ message }}</p> </body> </html>
-
在Flask应用程序中导入
render_template
函数,并在路由函数中调用该函数以渲染模板并返回响应。pythonfrom flask import Flask, render_template app = Flask(__name__) @app.route('/') def home(): title = 'Flask Template Engine' message = 'Hello, World!' return render_template('index.html', title=title, message=message) if __name__ == '__main__': app.run()
在视图函数中,定义了
title
和message
两个变量,并将其传递给render_template()
函数。render_template()
函数接收模板名称作为第一个参数,以及可选的关键字参数用于传递给模板的数据。render_template()
函数将根据模板文件中的Jinja2语法进行渲染,并返回渲染后的HTML内容作为响应发送给客户端。
通过使用Jinja2模板引擎,可以将静态HTML页面与动态生成的内容结合起来,实现更灵活、可维护和可重用的视图层逻辑。在模板中,可以使用条件语句、循环语句、过滤器等功能,对数据进行处理和展示,从而创建出更丰富的用户界面。
先简单将一讲Jinja2模板引擎的一些特性和用法【下面都会详细讲解~】:
-
模板语法:Jinja2使用
{``{ ... }}
来标记表达式,表示将变量插入到模板中。例如,{``{ name }}
将会在模板中显示变量name的值。而使用{% ... %}
来标记控制结构和语句,如条件语句、循环语句等。 -
变量:可以在模板中使用变量来动态地渲染内容。通过传递变量给模板进行渲染,例如:
render_template('index.html', name=name)
。在模板中,可以使用{``{ variable_name }}
语法来插入变量的值。 -
过滤器:Jinja2提供了许多内置的过滤器函数,用于对变量进行处理和格式化。例如,
{``{ name|capitalize }}
会将变量name的首字母大写。还可以自定义过滤器函数,使用@app.template_filter()
装饰器注册为模板过滤器。 -
控制结构:Jinja2支持各种控制结构,如if语句、循环语句和宏。通过使用
{% if ... %}...{% endif %}
来实现条件语句,使用{% for ... in ... %}...{% endfor %}
来实现循环语句,以及使用{% macro ... %}...{% endmacro %}
来定义宏。 -
继承:Jinja2允许模板之间进行继承,以提高代码的可重用性和可维护性。通过使用
{% extends ... %}
指令,一个模板可以继承另一个模板,并在其中使用{% block ... %}...{% endblock %}
来替换或扩展基础模板中的内容。 -
模板包含:Jinja2支持模板包含,允许将一个模板嵌入到另一个模板中。通过使用
{% include ... %}
指令,可以在模板中引用其他模板文件,实现代码的复用。 -
自定义全局变量和函数:可以在Flask应用中注册自定义的全局变量和函数,使其在所有模板中可用。通过使用
app.jinja_env.globals['variable_name'] = value
来注册全局变量【需要注意,在注册模板全局变量时,建议将其放置在一个在应用启动时运行的函数(如before_first_request
装饰的函数)中,以确保全局变量在所有请求中都可用】;使用@app.template_global()
装饰器来注册全局函数。
二、支持的Python数据类型:
Flask的Jinja2模板引擎支持以下Python数据类型:
- 字符串(String):用于表示文本数据。
- 整数(Integer):用于表示整数值,如1、2、3等。
- 浮点数(Float):用于表示带有小数部分的数值,如3.14、2.5等。
- 布尔值(Boolean):用于表示真或假的值,True或False。
- 列表(List):用于表示有序的集合,可以包含任意类型的元素。例如:
[1, 2, 'a', True]
。 - 元组(Tuple):类似于列表,但是是不可变的,也就是说不能修改元素的值。例如:
(1, 2, 'a', True)
。 - 字典(Dictionary):用于表示键值对的集合,其中每个元素由一个键和一个对应的值组成。例如:
{'name': 'John', 'age': 25}
。 - 集合(Set):用于表示无序且唯一的元素集合,不允许重复的元素。例如:
{'apple', 'banana', 'orange'}
。
除了上述基本数据类型之外,Jinja2模板还支持使用过滤器对数据进行处理和格式化,以及使用控制结构如条件语句和循环语句来控制模板的渲染流程。
需要注意的是,Jinja2模板引擎并不限制使用上述数据类型,可以在模板中使用任何Python支持的数据类型。如果有自定义的类或对象,也可以将其传递给模板,并在模板中访问其属性或调用其方法。
(1)实战在模板中使用Python常见的基本数据类型:
python
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/index", methods=['GET', 'POST'])
def index():
context = {
'k1': 123,
'k2': [11, 22, 33],
'k3': {'name': 'oldboy', 'age': 84}
}
return render_template('index.html', **context)
if __name__ == '__main__':
app.run()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ k1 }}</h1>
<h1>{{k2.0}} {{k2[0]}}</h1>
<h1>{{k3.name}} {{k3['name']}} {{k3.get('name', 888)}}</h1>
</body>
</html>
(2)实战在模板中使用匿名函数:
python
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/index", methods=['GET', 'POST'])
def index():
context = {
'k1': 123,
'k2': [11, 22, 33],
'k3': {'name': 'oldboy', 'age': 84},
'k4': lambda x: x+1
}
return render_template('index.html', **context)
if __name__ == '__main__':
app.run()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ k1 }}</h1>
<h1>{{k2.0}} {{k2[0]}}</h1>
<h1>{{k3.name}} {{k3['name']}} {{k3.get('name', 888)}}</h1>
<h1>{{k4(66)}}</h1>
</body>
</html>
(3)实战在模板中使用普通函数:
python
from flask import Flask, render_template
app = Flask(__name__)
def gen_input(value):
return "<input value='%s' />" %value
@app.route("/index", methods=['GET', 'POST'])
def index():
context = {
'k1': 123,
'k2': [11, 22, 33],
'k3': {'name': 'oldboy', 'age': 84},
'k4': lambda x: x+1,
'k5': gen_input
}
return render_template('index.html', **context)
if __name__ == '__main__':
app.run()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ k1 }}</h1>
<h1>{{k2.0}} {{k2[0]}}</h1>
<h1>{{k3.name}} {{k3['name']}} {{k3.get('name', 888)}}</h1>
<h1>{{k4(66)}}</h1>
<h1>{{k5(99)}}</h1>
</body>
</html>
如果不加 | safe,就直接显示字符串而不是input标签。这是Flask防止xss攻击。默认用户输入都是不安全的。
两种解决方法:
- 一种是前端加
| safe
; - 一种是后端使用markup。
html
<h1>{{k5(99) | safe}}</h1>
python
from flask import Flask, render_template, Markup
app = Flask(__name__)
def gen_input(value):
# return "<input value='%s' />" %value
return Markup("<input value='%s' />" %value)
三、template_global()
装饰器:
问题引入:
当我们在每一个模板里都想自定义一些函数: 如果每一个视图里都手动加,是不是太麻烦了!
解决方法:
-
这就引出了
template_global()
装饰器,使用它装饰的函数在每一个模板里都可以直接使用!!!template_global()
装饰器用于将一个函数注册为全局模板变量。被该装饰器修饰的函数可以在所有的模板中使用,并且不需要在每个视图函数中都传递一遍。
python
@app.template_global()
def sb(a1, a2):
return a1 + a2
html
<body>
<h1>{{sb(1,2)}}</h1>
</body>
需要注意的是,在使用template_global
装饰器时,函数名就是全局模板变量的名称。因此,在模板中调用全局模板变量时,直接使用函数名即可。
通过使用template_global
装饰器,我们可以方便地注册全局模板变量,提高代码的重用性和可维护性,并简化视图函数与模板之间的数据传递过程。
四、template_filter
装饰器:
-
常规用法:
template_filter
用于将一个函数注册为模板过滤器。被该装饰器修饰的函数可以在模板中通过过滤器的名称进行调用,并对数据进行处理和格式化。实战使用:
python
from flask import Flask, render_template
app = Flask(__name__)
@app.template_filter()
def reverse_string(string):
return string[::-1]
@app.route('/')
def home():
name = 'John Doe'
return render_template('index.html', name=name)
if __name__ == '__main__':
app.run()
在上述示例中,定义了一个名为reverse_string
的函数,并使用template_filter()
装饰器将其注册为模板过滤器。这个函数接受一个字符串参数,并返回反转后的字符串。
在home
视图函数中,将用户的姓名传递给模板,并在模板中使用{``{ name|reverse_string }}
来调用模板过滤器。这样,在模板中会自动将用户的姓名进行反转处理。
在使用template_filter
装饰器时,函数名就是模板过滤器的名称。因此,在模板中调用模板过滤器时,使用|
管道符号将变量与过滤器名称分隔开。
- 技巧使用:
python
from flask import Flask, render_template
app = Flask(__name__)
@app.template_filter()
def add_all(a1, a2, a3):
return a1 + a2 + a3
@app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ 1 | add_all(2,3) }}</h1>
</body>
</html>
五、Flask模板的继承:
Flask模板的继承是一种在多个模板中共享相同布局和结构的方式。通过使用模板的继承,我们可以定义一个基础模板,其中包含通用的 HTML 结构和样式,并在其他模板中继承这个基础模板,只需专注于特定内容的定义。
实战讲解:
base.html(基础模板):
html
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<header>
{% block header %}
<h1>Welcome to my website!</h1>
{% endblock %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}
© 2023 My Website - GuHanZhe. All rights reserved.
{% endblock %}
</footer>
</body>
</html>
home.html(继承自base.html):
html
{% extends 'base.html' %}
{% block title %}
Home Page
{% endblock %}
{% block content %}
<h2>Welcome to the Home Page!</h2>
<p>Content specific to the home page goes here.</p>
{% endblock %}
about.html(继承自base.html):
html
{% extends 'base.html' %}
{% block title %}
About Page
{% endblock %}
{% block content %}
<h2>About Us</h2>
<p>Content specific to the about page goes here.</p>
{% endblock %}
在上述示例中,base.html 是一个基础模板,定义了整个网页的结构和通用元素。其他模板(home.html 和 about.html)通过 {% extends 'base.html' %}
指令继承自 base.html,并可以使用 {% block %}
块来替换或扩展基础模板中的内容。
每个子模板都可以通过 {% block %}
块来定义自己特定的内容,这些块将在渲染时插入到相应的位置。例如,在 home.html 中的 {% block content %}
块中定义了特定于首页的内容。
通过使用模板继承,可以避免在每个模板中重复编写相同的 HTML 结构和样式,提高代码的可重用性和可维护性。