Python Web开发 Jinja2模板引擎
在之前的文章中,简单介绍了Python Web开发框架Flask,知道了如何写个Hello World,但是距离用Flask开发真正的项目,还有段距离,现在我们目标更靠近一些 —— 学习下Jinja2模板。
模板的作用
模板是用来做什么的呢?模板是用来更高效地生成相应时的Html文本的,没有模板,可以手写,比如之前的hello world示例,写段html代码:
-
<h1>Hello world!</h1>
对于简单的练习还行,但对于规模大的,动态化程度高的项目来说,这样写就有些勉强了,即,不利于项目和产品化。那么模板有什么好处呢:
-
能让展现逻辑和业务逻辑 展示逻辑即UI,就是用来给用户看和操作的,业务逻辑是业务规则,比如什么条件可以注册,什么权限能考到什么。模板将展现逻辑封装起来,业务逻辑写在视图函数中。
-
能使项目更易维护 由于展现逻辑和业务逻辑的分离,它们可以由不同的开发人员来维护,不会有代码冲突的问题
-
使项目更加安全 在做交互式开发中,有个原则: 永远不要相信用户的输入,因为恶意用户可能通过输入来注入(关于注入以后有机会可以单独聊聊),而模板在一定程度上会防注入,例如用户输入一点html代码作为输入,默认情况下模板会将其替换为网络安全字符,以防止恶意注入。
-
能提高开发效率 有了模板,相当于一个展示逻辑的函数,所以就可以被复用,可以用在不同的视图函数中,也可以用在不同的项目中
思考下:上面提到的展现逻辑和业务逻辑,为什么不直接说成前台和后台呢? 如果你有答案和想法,欢迎留言讨论。
Jinja2模板引擎
Jinja2是Flask框架默认支持的模板引擎,并不是唯一也不是最好(因人而异,没有最好)模板引擎,不同的Web框架,比如Django、Nodejs等都有自己的模板引擎,甚至一些程序员自己实现的模板引擎(我就这么干过),但大体思路是一样的,都是要将数据替换或者转换到,用特殊格式标记了位置的模板中,以合成动态的html,这种技术不新鲜,在之前的打印模板,如水晶报表里就有,无非就是标记和语法不同而已,所以要举一反三。
引入渲染函数
像其他功能一样,要使用模板引擎,先引入
-
from flask import render_template
注意:要将将模板文件放置在项目根目录(即
print(__file__)
显示的路径)下的templates
文件夹中
例如模板文件 hello.html
为:
-
<h1>Hello {{ name }} </h1>
视图函数可以写成:
-
@app.route('/user/<name>')
-
def index(name):
-
return render_template('hello.html', name=name)
Flask提供的 render_template
函数把Jinja2模板引擎集成到了程序中。render_template
函数第一个参数是模板的文件名,随后的参数都是键值对,表示模板中变量的对应的真实值,在上面代码中,模板会接收到一个名为 name
的变量
变量
模板文件就是普通的文本文件,然后将需要替换的部分用双大括号( {{}}
)标记出来,双大括号中,表示要替换的变量名,这个变量支持基本数据类型,以及列表、词典、对象和元组。如模板 template.html
:
-
<p> A value form a string: {{ name }}.</p>
-
<p> A value form a int: {{ myindex }}.</p>
-
<p> A value form a list: {{ mylist[3]] }}.</p>
-
<p> A value form a list, with a variable index: {{ mylist[myindex] }}.</p>
-
<p> A value form a dictionary: {{ mydict['key'] }}.</p>
-
<p> A value form a tuple: {{ mytuple }}.</p>
-
<p> A value form a tuple by index: {{ mytuple[myindex] }}.</p>
视图函数代码:
-
@app.route('/template/')
-
def template():
-
name = 'Jinja2 模板引擎'
-
myindex = 1
-
mylist = [1,2,3,4]
-
mydict = {
-
"key": 'age',
-
"value": '25'
-
}
-
mytuple = (1,2,3,4)
-
return render_template('template.html', name=name, myindex=myindex, mylist=mylist, mydict=mydict, mytuple=mytuple)
显示结果:
过滤器
有些时候需要对要在模板中替换的值做一些特殊处理,比如首字母大写,去掉前后空格等等,有种选择就是使用过滤器。
说明
Jinjia2模板引擎中,过滤器类似于Linux命令中的管道,例如将字符串变量的首字母大写
-
<h1>{{ name | capitalize}}</h1>
过滤器可以拼接,和linux的管道命令一样,如对值进行全部变大写,并且去除前后空白字符:
-
<h1>{{ name | upper | trim }}</h1>
如上代码,过滤器和变量之间用管道符号 | 相连,相当于对变量值作进一步加工。
一些常用的过滤器
过滤器 | 说明 |
---|---|
safe | 渲染是不转义 |
capitalize | 首字母大写 |
lower | 所有字母小写 |
upper | 所有字母大写 |
title | 值中每个单词首字母大写 |
trim | 删除首位空白字符 |
striptags | 渲染时删除掉值中所有HTML标签 |
注意:safe过滤器,默认情况下,处于安全考虑,Jinja2会转义所有变量,例如一个变量的值为 <h1>Hello</h1>
, Jinja2会将其渲染成 <h1>Hello</>
,浏览器会显示出原本的值,但是不会解释。如果需要浏览器解释的话,可以使用 safe 过滤器 例如模板文件 html.html
为:
-
<h1>{{ html | safe }}</h1>
视图函数为:
@app.route('/html')
def html():
return render_template('html.html', html='<b>bob</b>')
注意:千万别在不可信的值上使用 safe 过滤器,例如用户在表单上输入的文本。
还有一些有用的过滤器
-
default
,可以当变量未定义时,提供默认值,如果想将false
、False
和空(none
)视为未定义,需要提供第二个参数为true
{% raw %}当变量
name
的未定义时,上下两个显示效果一样,当值为none
时,上面会显示Hellonone!
, 而下面的会显示Helloworld!
-
<!-- 提供默认值过滤器 -->
-
<h1>Hello {{ name | default('world') }}!</h1>
-
<!-- 将false、False和空(none)视为未定义的默认值过滤器 -->
-
<h1>Hello {{ name | default('world', true)! }}</h1>
-
-
列表过滤器
min
,max
, 得到列表中的最小值或最大值
自定义过滤器
过滤器虽然有很多,但总有不满足需求的时候,例如首行文字缩进、将金额转化为中文的大写等等。过滤器实质就是个函数,所以,第一定义一个过滤器函数,第二,注册到Jinjia2的过滤器中。
#自定义过滤器函数
def mylen(arg):#实现一个可以求长度的函数return len(arg)
def interval(test_str, start, end):#返回字符串中指定区间的内容return test_str[int(start):int(end)]
#注册过滤器
env = app.jinja_env
env.filters['mylen'] = mylen
env.filters['interval'] = interval@app.route('/myfilter')
def myfilter():return render_template('myfilter.html', phone = '135645xxx623')
模板
-
<h1>电话号码是:{{ phone }}, 长度为:{{ phone | mylen }},运营商号:{{ phone | interval(0,3) }}</h1>
过滤器注册代码还可以写在初始化代码
__init__.py
中
控制结构
很多时候,需要更智能的模板渲染,即能给渲染编程,比如男生一个样式,女生一样样式,控制结构指令需要用指令标记来指定,下面介绍下一些简单的控制结构
条件
即在模板中用 if-else
控制结构
{% if gender=='male' %}
Hello, Mr {{ name }}
{% else %}
Hello, Ms {{ name }}
{% endif %}
视图函数
@app.route('/hello2/<name>/<gender>')
def hello2(name, gender):
return render_template('hello2.html', name=name, gender=gender)
在控制结构里,代码语法同 python
循环
循环对于渲染列表,很有帮助,循环的标记是 for
。例如奖列表的内容显示在 ul
中
<ul>
{% for name in names %}
<li>{{ name }} </li>
{% endfor %}
</ul>
例如给定一个学生列表,将其用无序列表 ul
显示出来
宏——模板中的函数
模板中可以定义宏,相当于定义了一个函数,可以重复使用,让逻辑更清晰。首先,定义一个宏:
{% macro render_name(name) %}
<li>{{ name }}</li>
{% endmacro %}
{% endraw %} 然后使用宏, 例如将循环结构的例子中,显示名称的地方,改为调用宏 {% raw %}
<ul>
{% for name in names %}
{{ render_name(name) }}
{% endfor %}
</ul>
调用宏,和调用函数是一样的,不过要将代码写在 {{}}
双大括号内。一般我们会将宏存在单独的文件中,以便复用,在需要用到宏的地方,引用就好了
{% import 'mymarco.html' as macros %}
<ul>
{% for name in names %}
{{ macros.render_name(name) }}
{% endfor%}
</ul>
如上所述,用improt引入宏定义文件,通过as指定别名,和python的模块引入一样。指定别名是一个良好的编程习惯,可以将一个复杂的东西形象化,同时像一个命名空间一样,有效的避免冲突。
include
另外可以将多个模板片段写入一个单独文件,再包含( include
)在所有模板中,以提高开发效率:
-
{% include 'common.html' %}
include
进来的文件,相当于将文件中的内容复制到 include
的位置,所以自使用之前需要考虑仔细
模板继承
如果觉得 include
过于呆板,灵活性差,Jinja2模板引擎还有更高级的功能——继承。类似于Python代码中类的继承,一起看看。首先定义一个基类, base.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8">{% block head %}<title>{% block title %}{% endblock %} - My Application</title>{% endblock %}
</head>
<body>{% block body %}<h3>这是基类的内容</h3>{% endblock %}
</body>
</html>
其中的 block
标签,定义了可以被子类重构(替换)的部分,每个 blcok
标签,需要指定一个特殊的名称,例如 head
、 title
等,以便子类用特定的名称来重构。另外 block
标签需要有结束标签 endblock
,类似于类C语言中的大括号,当然 block
标签也可以嵌套。接下来,定义一个子类模板 hello3.html:
<!DOCTYPE html>
<html lang="en">
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}{{ super()}}<style></style>
{% endblock %}
{% block body %}{{ super()}}<h4>这是子类的内容Hello world</h4>
{% endblock %}
</html>
@app.route('/hello3')
def hello3():return render_template('hello3.html')
效果如图所示:
通过 extends
标记来指定需要继承的基类,然后用 block
标记来设置子类需要替换调基类中的内容,只要 block
指定的名称一样就行。另外,如不需要完全替换调基类的内容,可以在子类 block
中,调用 super
方法,以获取基类在此名称下的内容,这样就能达到更号的灵活性。
总结
今天介绍了Jinja2模板引擎的基本用法和特点,期望通过不同的特点,让你了解到模板的基本用法,以便更快的使用和进一步学习更深入的内容。另外,想通过Jinja2模板引擎,说明模板的基本特征,以便触类旁通、举一反三,更快的学习其他优秀的模板, 同时也想说明,模板不仅仅可以用在Web的开发中,还可以用在自动化编码、测试等众多领域。
相关文章:

Python Web开发 Jinja2模板引擎
在之前的文章中,简单介绍了Python Web开发框架Flask,知道了如何写个Hello World,但是距离用Flask开发真正的项目,还有段距离,现在我们目标更靠近一些 —— 学习下Jinja2模板。 模板的作用 模板是用来做什么的呢&…...
ubuntu上安装mosquitto服务
1、mosquitto是什么 Mosquitto 项目最初由 IBM 和 Eurotech 于 2013 年开发,后来于 2016 年捐赠给 Eclipse 基金会。Eclipse Mosquitto 基于 Eclipse 公共许可证(EPL/EDL license)发布,用户可以免费使用。作为全球使用最广的 MQTT 协议实现之一 &#x…...

嵌入式开发学习(STC51-9-led点阵)
内容 点亮一个点; 显示数字; 显示图像; LED点阵简介 LED 点阵是由发光二极管排列组成的显示器件 通常应用较多的是8 * 8点阵,然后使用多个8 * 8点阵可组成不同分辨率的LED点阵显示屏,比如16 * 16点阵可以使用4个8 *…...

RedisTemplate.opsForZSet()用法简介并举例
RedisTemplate.opsForZSet()是RedisTemplate类提供的用于操作ZSet类型(有序集合)的方法。它可以用于对Redis中的ZSet数据结构进行各种操作,如添加成员、获取成员、删除成员等。 下面是一些常用的RedisTemplate.opsForZSet()方法及其用法示例…...

Java个人博客系统--基于Springboot的设计与实现
目录 一、项目概述 应用技术 接口实现: 数据库定义: 数据库建表: 博客表数据库相关操作: 添加项⽬公共模块 加密MD5 页面展示:http://121.41.168.121:8080/blog_login.html 项目源码:https://gitee…...

在jupyter中下载数据集失败及解决方法(以IMDB为例)
在IMDB数据集下载时,由于网络原因下载失败,报错如下: Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz ConnectionResetError Traceback (most recent call last) … Exception: URL fetch f…...

【设计模式】-工厂方法模式
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它通过定义一个用于创建对象的接口,但是将具体对象的创建推迟到子类中。这样,子类可以决定要实例化的对象类型。工厂方法模式提供了一种方式,通…...

H7-TOOL的高速DAPLINK用于新版STM32CubeIDE V1.13及其以上版本的超简单实现方法(2023-08-08)
之前分享了一个方法,太繁琐了,H7-TOOL群的群友提供了一个方法,实现非常简单。1、使用STM32CubeMX或者自己创建一个STM32CubeIDE工程后,设置这两个地方即可: 配置调试器,设置完毕记得点击右下角的Apply 2、然…...

成功解决ubuntu-22.04的sudo apt-get update一直卡在【0% [Waiting for headers]】
成功解决ubuntu-22.04的sudo apt-get update一直卡在【0% [Waiting for headers]】 问题描述解决方案 问题描述 在下载安装包的时候一直卡在0% [Waiting for headers],报错信息如下: Get:1 file:/var/cudnn-local-repo-ubuntu1804-8.5.0.96 InRelease […...

openLayers实战(一):vue项目中的离线地图引入
最近的项目涉及到离线地图的操作,查阅社区文章,决定使用openLayersvue离线地图的方式进行开发,前期基础引入操作完全参考掘金文章,非常优秀全面的文章。 openlayers 实战离线地图 - 掘金 此外,开发过程的地图操作可参考…...

如何构造一个安全的单例?
为什么要问这个问题? 我们知道,单例是一种很常用的设计模式,主要作用就是节省系统资源,让对象在服务器中只有一份。但是实际开发中可能有很多人压根没有写过单例这种模式,只是看过或者为了面试去写写demo熟悉一下。那…...

单片机开发 esp8266
一、固件界面 二、项目介绍 固件名称:esp8266-universalboard v1.0 提供商: 半条虫(466814195) 下载:esp8266-universalboard.bin 源码地址:Gitlab...

Linux 查看版本和用户权限提升实践心得
文章目录 linux (Ubuntu内核)查看版本版本信息解释内置yum工具?用户权限提升操作步骤 查看deepin系统的版本和其debian的版本遇到的问题:deepin-release文件不存在 linux (Ubuntu内核)查看版本 使用lsb_release命令: lsb_release -a该命令将…...

多线程编程5:线程同步和进程通信(C++11和linux)
常见的线程同步 linux: 互斥锁:实现共享资源的串行访问,有三个版本普通锁(默认属性),检错锁(可以防止相同线程重复加锁)和递归锁(相同线程可以重复加锁)条件变量:配合互斥锁使用,实现线程之间的通信&#…...

tensorrt官方int8量化方法汇总
原理及操作 量化的基本原理及流程可参看懂你的神经网络量化教程:第一讲、量化番外篇、TensorRT中的INT8、tensorRT int8量化示例代码 Tensorrt 方式1:trtexec(PTQ的一种) int8量化 trtexec --onnxXX.onnx --saveEnginemodel.…...

21、p6spy输出执行SQL日志
文章目录 1、背景2、简介3、接入3.1、 引入依赖3.2、修改database参数:3.3、 创建P6SpyLogger类,自定义日志格式3.4、添加spy.properties3.5、 输出样例 4、补充4.1、参数说明 1、背景 在开发的过程中,总希望方法执行完了可以看到完整是sql语…...

实力认证!TDengine 入选 Gartner 中国数据分析与人工智能技术成熟度曲线
近日,国际权威研究机构 Gartner 发布了《2023 年中国数据分析及人工智能技术成熟度曲线》(即《Hype Cycle for Data, Analytics and AI in China, 2023》)报告,TDengine 成功入选实时数据管理领域代表产品。 作为评估全球新技术成…...

如何将jar包部署到宝塔
尝试多种方式上传,但启动一直失败,这种方式亲测是好使的 项目内修改位置 在pom.xml文件中将mysql的scope改成provided,如果是固定的版本号会出现问题 之后就可以打包啦,直接点击maven中的package 找到打包文件的位置ÿ…...

el-tree-select那些事
下拉菜单树形选择器 用于记录工作及日常学习涉及到的一些需求和问题 vue3 el-tree-select那些事 1、获取el-tree-select选中的任意层级的节点对象 1、获取el-tree-select选中的任意层级的节点对象 1-1数据集 1-2画面 1-3代码 1-3-1画面代码 <el-tree-selectv-model"s…...

分布式任务调度框架之开山鼻祖:Quartz
1.简介 最近我司上线使用了分布式任务调度框架:XXL-JOB,方便对任务的管理控制。本来一开始就想讲述一下该框架,但是在学习了解过程中发现该框架式基于Quartz思想开发实现的,Quartz 是一个很火的开源任务调度框架,完全…...

Django实现音乐网站 ⑺
使用Python Django框架制作一个音乐网站, 本篇主要是后台对歌手原有实现功能的基础上进行优化处理。 目录 新增编辑 表字段名称修改 隐藏单曲、专辑数 姓名首字母 安装xpinyin 获取姓名首字母 重写保存方法 列表显示 图片显示处理 引入函数 路径改为显示…...

PO、DTO和VO的区别
PO:Persistence Object,是持久化对象,也叫实体对象(Entity),是对象到关系表的映射。在关系型数据库中,每张表对应着一个PO,PO 中的属性值与表中数据一一对应,通过数据访问…...

ReadableStream流的js处理
需求 今天接了一个gpt的项目。需要对接接口。后端返回的数据是ReadableStream类型的。记录下如何处理ReadableStream类型的数据 代码 const response ... // response 是服务端返回的数据// 创建了一个数据读取器 const reader response.getReader(); // 创建了一个文本解…...

图扑软件入选 2023 中国信通院“铸基计划”全景图
7 月 27 日,由中国信通院主办的“2023 数字生态发展大会”暨中国信通院“铸基计划”年中会议在北京召开。本次大会重磅发布了《高质量数字化转型产品及服务全景图(2023 上半年度)》。图扑软件凭借自研 HT for Web 数字孪生可视化产品成功入选…...

Go Fyne:一种现代的,跨平台的 GUI 开发工具集
Fyne 是一个用 Go 语言写的,提供丰富,现代化的用户界面(UI)的开发工具集。Fyne 提供了一种简单而直观的方式来构建应用程序,并且注意到它是跨平台的,可以在 Windows,macOS,Linux&…...

React Dva 操作models中的subscriptions讲述监听
接下来 我们来看一个models的属性 之前没有讲到的subscriptions 我们可以在自己有引入的任意一个models文件中这样写 subscriptions: {setup({ dispatch, history }) {console.log(dispatch);}, },这样 一进来 这个位置就会触发 这里 我们可以写多个 subscriptions: {setup…...

标准化归一化 batch norm, layer norm, group norm, instance norm
Layer Normalization - EXPLAINED (in Transformer Neural Networks) Layer Normalization - EXPLAINED (in Transformer Neural Networks) 0~4min:什么是multi-head attention 5~7min:layer norm图示 7~9min:公式举例layer norm 9:54-end:layer norm的代码示例 group n…...

Zabbix监控系统详解及配置
前言 作为一个运维,需要会使用监控系统查看服务器状态以及网站流量指标,利用监控系统的数据去了解上线发布的结果,和网站的健康状态。利用一个优秀的监控软件,我们可以: 通过一个友好的界面进行浏览整个网站所有的服务…...

【100天精通python】Day29:文件与IO操作_XML文件处理
目录 专栏导读 一、XML文件概述 1. 标签和元素 2. 嵌套结构 3. 属性 4. 命名空间 5. CDATA节 6. 注释 7. 验证与验证语言 8. 扩展性 二、XML文件处理常见操作 1. 解析XML文件 2. 创建和编辑XML文件 3. 修改XML文件 4. 查询XML元素 5 遍历XML元素 6. 删除XML元…...

人工智能的未来:探索下一代生成模型
推荐:使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 生成式 AI 目前能够做什么,以及探索下一波生成式 AI 模型需要克服的当前挑战? 如果你跟上科技世界的步伐,你就会知道生成式人工智能是最热门的话题。我们听到了很多关于…...