第21天:Web开发 Jinja2模板引擎

简介: 第21天:Web开发 Jinja2模板引擎

在之前的文章中,简单介绍了Python Web开发框架Flask,知道了如何写个Hello World,但是距离用Flask开发真正的项目,还有段距离,现在我们目标更靠近一些 —— 学习下Jinja2模板。


模板的作用


模板是用来做什么的呢?模板是用来更高效地生成相应时的Html文本的,没有模板,可以手写,比如之前的hello world示例,写段html代码:

  1. Hello world!



对于简单的练习还行,但对于规模大的,动态化程度高的项目来说,这样写就有些勉强了,即,不利于项目和产品化。那么模板有什么好处呢:


  1. 能让展现逻辑和业务逻辑 展示逻辑即UI,就是用来给用户看和操作的,业务逻辑是业务规则,比如什么条件可以注册,什么权限能考到什么。模板将展现逻辑封装起来,业务逻辑写在视图函数中。


  1. 能使项目更易维护 由于展现逻辑和业务逻辑的分离,它们可以由不同的开发人员来维护,不会有代码冲突的问题



  1. 使项目更加安全 在做交互式开发中,有个原则: 永远不要相信用户的输入,因为恶意用户可能通过输入来注入(关于注入以后有机会可以单独聊聊),而模板在一定程度上会防注入,例如用户输入一点html代码作为输入,默认情况下模板会将其替换为网络安全字符,以防止恶意注入。


  1. 能提高开发效率 有了模板,相当于一个展示逻辑的函数,所以就可以被复用,可以用在不同的视图函数中,也可以用在不同的项目中


思考下:上面提到的展现逻辑业务逻辑,为什么不直接说成前台后台呢? 如果你有答案和想法,欢迎留言讨论。


Jinja2模板引擎


Jinja2是Flask框架默认支持的模板引擎,并不是唯一也不是最好(因人而异,没有最好)模板引擎,不同的Web框架,比如Django、Nodejs等都有自己的模板引擎,甚至一些程序员自己实现的模板引擎(我就这么干过),但大体思路是一样的,都是要将数据替换或者转换到,用特殊格式标记了位置的模板中,以合成动态的html,这种技术不新鲜,在之前的打印模板,如水晶报表里就有,无非就是标记和语法不同而已,所以要举一反三。


引入渲染函数


像其他功能一样,要使用模板引擎,先引入

  1. from flask import render_template

注意:要将将模板文件放置在项目根目录(即 print(__file__)显示的路径)下的 templates文件夹中


例如模板文件 hello.html为:

  1. Hello {{ name }}


{% endraw %}



视图函数可以写成:

  1. @app.route('/user/')
  2. def index(name):
  3.    return render_template('hello.html', name=name)

Flask提供的 render_template函数把Jinja2模板引擎集成到了程序中。render_template函数第一个参数是模板的文件名,随后的参数都是键值对,表示模板中变量的对应的真实值,在上面代码中,模板会接收到一个名为 name的变量



变量


模板文件就是普通的文本文件,然后将需要替换的部分用双大括号( {{}})标记出来,双大括号中,表示要替换的变量名,这个变量支持基本数据类型,以及列表、词典、对象和元组。如模板 template.html:

  1. A value form a string: {{ name }}.


  2. A value form a int: {{ myindex }}.


  3. A value form a list: {{ mylist[3]] }}.


  4. A value form a list, with a variable index: {{ mylist[myindex] }}.


  5. A value form a dictionary: {{ mydict['key'] }}.


  6. A value form a tuple: {{ mytuple }}.


  7. A value form a tuple by index: {{ mytuple[myindex] }}.



{% endraw %}



视图函数代码:



  1. @app.route('/template/')
  2. def template():
  3.    name ='Jinja2 模板引擎'
  4.    myindex =1
  5.    mylist =[1,2,3,4]
  6.    mydict ={
  7.       key:'age',
  8.       value:'25'
  9.    }
  10.    mytuple =(1,2,3,4)
  11.    return render_template('template.html', name=name, myindex=myindex, mylist=mylist, mydict=mydict, mytuple=mytuple)



显示结果:


image.png


过滤器


有些时候需要对要在模板中替换的值做一些特殊处理,比如首字母大写,去掉前后空格等等,有种选择就是使用过滤器。


说明


Jinjia2模板引擎中,过滤器类似于Linux命令中的管道,例如将字符串变量的首字母大写

{% raw %}


  1. {{ name | capitalize}}


{% endraw %}

过滤器可以拼接,和linux的管道命令一样,如对值进行全部变大写,并且去除前后空白字符:

{% raw %}

  1. {{ name | upper | trim }}


{% endraw %}


如上代码,过滤器和变量之间用管道符号 | 相连,相当于对变量值作进一步加工。

一些常用的过滤器


过滤器 说明
safe 渲染是不转义
capitalize 首字母大写
lower 所有字母小写
upper 所有字母大写
title 值中每个单词首字母大写
trim 删除首位空白字符
striptags 渲染时删除掉值中所有HTML标签

注意:safe过滤器,默认情况下,处于安全考虑,Jinja2会转义所有变量,例如一个变量的值为



Hello



, Jinja2会将其渲染成 <h1>Hello</>,浏览器会显示出原本的值,但是不会解释。如果需要浏览器解释的话,可以使用 safe 过滤器 例如模板文件 html.html为:

{% raw %}

  1. {{ html | safe }}


{% endraw %}



视图函数为:


  1. @app.route('/html')
  2. def html():
  3.    return render_template('html.html', html='bob')

注意:千万别在不可信的值上使用 safe 过滤器,例如用户在表单上输入的文本。

还有一些有用的过滤器



  • default,可以当变量未定义时,提供默认值,如果想将 falseFalse和空( none)视为未定义,需要提供第二个参数为 true{% raw %}
    {% endraw %}


  • 当变量 name的未定义时,上下两个显示效果一样,当值为 none时,上面会显示 Hellonone!, 而下面的会显示 Helloworld!


  1. Hello {{ name | default('world') }}!




  2. Hello {{ name | default('world', true)! }}



  • 列表过滤器 min, max, 得到列表中的最小值或最大值

自定义过滤器


过滤器虽然有很多,但总有不满足需求的时候,例如首行文字缩进、将金额转化为中文的大写等等。过滤器实质就是个函数,所以,第一定义一个过滤器函数,第二,注册到Jinjia2的过滤器中。


  1. # 定义过滤器函数
  2. def mylen(arg):#实现一个可以求长度的函数
  3.    return len(arg)
  4. def interval(test_str, start, end):# 返回字符串中指定区间的内容
  5.    return test_str[int(start):int(end)]

  6. # 注册过滤器
  7. env = app.jinja_env
  8. env.filters['mylen']= mylen
  9. env.filters['interval']= interval

  10. # 视图函数
  11. @app.route('/myfilter')
  12. def myfilter():
  13.    return render_template('myfilter.html', phone='13300000000')

模板文件



{% raw %}


  1. 电话号码是:{{ phone }}, 长度为:{{ phone | mylen }},运营商号:{{ phone | interval(0,3) }}


{% endraw %}

过滤器注册代码还可以写在初始化代码 __init__.py

控制结构

很多时候,需要更智能的模板渲染,即能给渲染编程,比如男生一个样式,女生一样样式,控制结构指令需要用指令标记来指定,下面介绍下一些简单的控制结构

条件


即在模板中用 if-else控制结构


{% raw %}

  1. {%if gender=='male'%}
  2.    Hello,Mr{{ name }}
  3. {%else%}
  4.    Hello,Ms{{ name }}
  5. {% endif %}

{% endraw %}

视图函数

  1. @app.route('/hello2//')
  2. def hello2(name, gender):
  3.    return render_template('hello2.html', name=name, gender=gender)

在控制结构里,代码语法同 python


循环


循环对于渲染列表,很有帮助,循环的标记是 for。例如奖列表的内容显示在 ul

{% raw %}


    • {% for name in names %}
    •    
    • {{ name }}

    • {% endfor %}


{% endraw %}


例如给定一个学生列表,将其用无序列表 ul显示出来


宏——模板中的函数


模板中可以定义宏,相当于定义了一个函数,可以重复使用,让逻辑更清晰。首先,定义一个宏:


mymacro.html {% raw %}


  1. {% macro render_name(name)%}
  2.    
  3. {{ name }}li>
  4. {% endmacro %}

{% endraw %} 然后使用宏, 例如将循环结构的例子中,显示名称的地方,改为调用宏 {% raw %}


    •    {% for name in names %}
    •        {{ render_name(name) }}
    •    {% endfor %}


{% endraw %}

调用宏,和调用函数是一样的,不过要将代码写在 {{}}双大括号内。一般我们会将宏存在单独的文件中,以便复用,在需要用到宏的地方,引用就好了

{% raw %}

  1. {%import'mymarco.html'as macros %}

    •    {%for name in names %}
    •        {{ macros.render_name(name)}}
    •    {% endfor%}
    • ul>

{% endraw %}

如上所述,用improt引入宏定义文件,通过as指定别名,和python的模块引入一样。指定别名是一个良好的编程习惯,可以将一个复杂的东西形象化,同时像一个命名空间一样,有效的避免冲突。

include

另外可以将多个模板片段写入一个单独文件,再包含( include)在所有模板中,以提高开发效率:

{% raw %}

  1. {% include 'common.html'%}

{% endraw %}

include进来的文件,相当于将文件中的内容复制到 include的位置,所以自使用之前需要考虑仔细

模板继承

如果觉得 include过于呆板,灵活性差,Jinja2模板引擎还有更高级的功能——继承。类似于Python代码中类的继承,一起看看。首先定义一个基类, base.html: {% raw %}



  1.    {% block head %}
  2.    </span><span style="color: #1B1918;">{% block title %}{% endblock%} - My Application</span><span style="color: #F22C40;">
  3.    {% endblock %}


  4.    {% block body %}
  5.        

    这是基类的内容


  6.    {% endblock %}


{% endraw %}

其中的 block标签,定义了可以被子类重构(替换)的部分,每个 blcok标签,需要指定一个特殊的名称,例如 headtitle等,以便子类用特定的名称来重构。另外 block标签需要有结束标签 endblock,类似于类C语言中的大括号,当然 block标签也可以嵌套。接下来,定义一个子类模板 hello3.html:

{% raw %}

  1. {%extends"base.html"%}
  2. {% block title %}Index{% endblock %}
  3. {% block head %}
  4.    {{super()}}
  5.    </span><span style="color: #1B1918;"></</span><span style="color: #1B1918;">style</span><span style="color: #1B1918;">></span></code></span><br /></li><li><span style="color: #4A4A4A;"><code><span style="color: #1B1918;">{%</span><span style="color: #1B1918;"> endblock </span><span style="color: #1B1918;">%}</span></code></span><br /></li><li><span style="color: #4A4A4A;"><code><span style="color: #1B1918;">{%</span><span style="color: #1B1918;"> block body </span><span style="color: #1B1918;">%}</span></code></span><br /></li><li><span style="color: #4A4A4A;"><code><span style="color: #1B1918;">    </span><span style="color: #1B1918;">{{</span><span style="color: #1B1918;"> </span><span style="color: #6666EA;">super</span><span style="color: #1B1918;">()</span><span style="color: #1B1918;"> </span><span style="color: #1B1918;">}}</span></code></span><br /></li><li><span style="color: #4A4A4A;"><code><span style="color: #1B1918;">    </span><span style="color: #7B9726;"><h3></span><span style="color: #1B1918;">这是子类的内容</span><span style="color: #1B1918;"> </span><span style="color: #407EE7;">Hello</span><span style="color: #1B1918;"> world</span><span style="color: #1B1918;">!</</span><span style="color: #1B1918;">h</span><span style="color: #1B1918;">></span></code></span><br /></li><li><span style="color: #4A4A4A;"><code><span style="color: #1B1918;">{%</span><span style="color: #1B1918;"> endblock </span><span style="color: #1B1918;">%}</span></code></span><br /></li></ol><div>{% endraw %}</div><div>效果如图所示:</div><div><br /></div><div><br /></div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F183ffca4febd49db8154759e72f6b3e4.png%22%2C%22originWidth%22%3A759%2C%22originHeight%22%3A285%2C%22name%22%3A%22image.png%22%2C%22size%22%3A101360%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A379.5%2C%22height%22%3A143%7D"></span></div><div><br /></div><div>通过 <code><span>extends</span></code>标记来指定需要继承的基类,然后用 <code><span>block</span></code>标记来设置子类需要替换调基类中的内容,只要 <code><span>block</span></code>指定的名称一样就行。另外,如不需要完全替换调基类的内容,可以在子类 <code><span>block</span></code>中,调用 <code><span>super</span></code>方法,以获取基类在此名称下的内容,这样就能达到更号的灵活性。</div><h3 id="80N0X">总结</h3><div>今天介绍了Jinja2模板引擎的基本用法和特点,期望通过不同的特点,让你了解到模板的基本用法,以便更快的使用和进一步学习更深入的内容。另外,想通过Jinja2模板引擎,说明模板的基本特征,以便触类旁通、举一反三,更快的学习其他优秀的模板, 同时也想说明,模板不仅仅可以用在Web的开发中,还可以用在自动化编码、测试等众多领域。最后在本章开头,留了个思考题,为什么不将<strong>展现逻辑</strong>和<strong>业务逻辑</strong>说成是<strong>前台</strong>和<strong>后台</strong>呢?如果你有答案,欢迎留言交流。</div><h3 id="79bk2">参考</h3><ul style="text-align: justify;"><li><span style="color: #4A4A4A;">图书: Flask Web开发 </span><span style="color: #0080FF;"><a href="https://item.jd.com/12418677.html" target="_blank">https://item.jd.com/12418677.html</a></span><br /></li><li><span style="color: #4A4A4A;">API-Jinja Documentation(2,10.x) </span><span style="color: #0080FF;"><a href="https://jinja.palletsprojects.com/en/2.10.x/api/#the-context" target="_blank">https://jinja.palletsprojects.com/en/2.10.x/api/#the-context</a></span><br /></li><li><span style="color: #4A4A4A;"><span>ansible基础-Jinjia2模板——过滤器 </span></span><br /><span style="color: #0080FF;"><a href="https://www.cnblogs.com/mauricewei/p/10056379.html" target="_blank">https://www.cnblogs.com/mauricewei/p/10056379.html</a></span><br /></li></ul>
目录
相关文章
|
10天前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
98 4
|
4月前
|
缓存 JavaScript 前端开发
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
本文深入解读鸿蒙官方文档中的 `ArkWeb` 性能优化技巧,从预启动进程到预渲染,涵盖预下载、预连接、预取POST等八大优化策略。通过代码示例详解如何提升Web页面加载速度,助你打造流畅的HarmonyOS应用体验。内容实用,按需选用,让H5页面快到飞起!
|
4月前
|
JavaScript 前端开发 API
鸿蒙5开发宝藏案例分享---Web加载时延优化解析
本文深入解析了鸿蒙开发中Web加载完成时延的优化技巧,结合官方案例与实际代码,助你提升性能。核心内容包括:使用DevEco Profiler和DevTools定位瓶颈、四大优化方向(资源合并、接口预取、图片懒加载、任务拆解)及高频手段总结。同时提供性能优化黄金准则,如首屏资源控制在300KB内、关键接口响应≤200ms等,帮助开发者实现丝般流畅体验。
|
前端开发 JavaScript Shell
鸿蒙5开发宝藏案例分享---Web页面内点击响应时延分析
本文为鸿蒙开发者整理了Web性能优化的实战案例解析,结合官方文档深度扩展。内容涵盖点击响应时延核心指标(≤100ms)、性能分析工具链(如DevTools时间线、ArkUI Trace抓取)以及高频优化场景,包括递归函数优化、网络请求阻塞解决方案和setTimeout滥用问题等。同时提供进阶技巧,如首帧加速、透明动画陷阱规避及Web组件初始化加速,并通过优化前后Trace对比展示成果。最后总结了快速定位问题的方法与开发建议,助力开发者提升Web应用性能。
|
4月前
|
JSON 开发框架 自然语言处理
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)
本文主要介绍了应用开发中的三大核心内容:生命周期管理、资源限定与访问以及多语言支持。在生命周期部分,详细说明了应用和页面的生命周期函数及其触发时机,帮助开发者更好地掌控应用状态变化。资源限定与访问章节,则聚焦于资源限定词的定义、命名规则及匹配逻辑,并阐述了如何通过 `$r` 引用 JS 模块内的资源。最后,多语言支持部分讲解了如何通过 JSON 文件定义多语言资源,使用 `$t` 和 `$tc` 方法实现简单格式化与单复数格式化,为全球化应用提供便利。
209 104
|
4月前
|
JavaScript 前端开发 API
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(二)
本文介绍了HarmonyOS应用开发中的HML、CSS和JS语法。HML作为标记语言,支持数据绑定、事件处理、列表渲染等功能;CSS用于样式定义,涵盖尺寸单位、样式导入、选择器及伪类等特性;JS实现业务逻辑,包括ES6语法支持、对象属性、数据方法及事件处理。通过具体代码示例,详细解析了页面构建与交互的实现方式,为开发者提供全面的技术指导。
235 104
|
4月前
|
开发框架 编解码 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(一)
该文档详细介绍了一个兼容JS的类Web开发范式的方舟开发框架,涵盖概述、文件组织、js标签配置及app.js等内容。框架采用HML、CSS、JavaScript三段式开发方式,支持单向数据绑定,适合中小型应用开发。文件组织部分说明了目录结构、访问规则和媒体文件格式;js标签配置包括实例名称、页面路由和窗口样式信息;app.js则描述了应用生命周期与对象管理。整体内容旨在帮助开发者快速构建基于方舟框架的应用程序。
216 102
|
5月前
|
Web App开发 前端开发 JavaScript
鸿蒙5开发宝藏案例分享---Web适配一多开发实践
这是一份实用的鸿蒙Web多设备适配开发指南,针对开发者在不同屏幕尺寸下的布局难题提供了解决方案。文章通过三大法宝(相对单位、媒体查询和窗口监听)详细介绍如何实现智能适配,并提供了多个实战案例,如宫格布局、对话框变形和自适应轮播图等。此外,还分享了调试技巧及工具推荐,帮助开发者快速上手并优化性能。最后鼓励读者实践探索,并提示更多官方资源等待发现。
|
7月前
|
关系型数据库 MySQL 数据库
基于Flink CDC 开发,支持Web-UI的实时KingBase 连接器,三大模式无缝切换,效率翻倍!
TIS 是一款基于Web-UI的开源大数据集成工具,通过与人大金仓Kingbase的深度整合,提供高效、灵活的实时数据集成方案。它支持增量数据监听和实时写入,兼容MySQL、PostgreSQL和Oracle模式,无需编写复杂脚本,操作简单直观,特别适合非专业开发人员使用。TIS率先实现了Kingbase CDC连接器的整合,成为业界首个开箱即用的Kingbase CDC数据同步解决方案,助力企业数字化转型。
1333 5
基于Flink CDC 开发,支持Web-UI的实时KingBase 连接器,三大模式无缝切换,效率翻倍!
|
7月前
|
机器学习/深度学习 开发框架 API
Python 高级编程与实战:深入理解 Web 开发与 API 设计
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧以及数据科学和机器学习。本文将深入探讨 Python 在 Web 开发和 API 设计中的应用,并通过实战项目帮助你掌握这些技术。