1.Flask模板介绍
前置:理解渲染机制即上篇笔记中render_template()功能是如何实现的!
1)找到html文件地址
2)读取html文件中的内容
3)替换html中的特殊字符
4)将html的内容发送给浏览器
1 <!-- html文件 --> 2 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <title>Title</title> 8 </head> 9 <body> 10 <nav> 11 导航条 12 </nav> 13 <h1 style="color: #f00">你好, 当前的时间是:{{time}} {{user}}</h1> 14 </body> 15 </html>
1 # Flask py文件 2 3 import os 4 from datetime import datetime 5 6 from flask import Flask,render_template 7 app = Flask(__name__) 8 9 10 @app.route('/') 11 def index(): 12 return 'index' 13 14 15 @app.route('/html') 16 def html_from_file(): 17 """ 把html文件的内容在浏览器展现出来""" 18 return render_template('index.html') 19 20 21 @app.route('/show/html') 22 def html_show(): 23 """ 理解渲染机制 """ 24 # 1. 找到磁盘上的html文件地址(全路径) 25 file_name = os.path.join(os.path.dirname(__file__), 'templates', 'index.html') 26 print(file_name) 27 # 2. 读取html文件中的内容 28 now_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 29 with open(file_name, 'r', encoding='utf-8') as f: 30 html = f.read() 31 # 3. 替换html中的特殊字符({{time}}) 32 html = html.replace('{{time}}', now_time) 33 # 4. 将html的内容发送给浏览器 34 return html
什么是模板?
- 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
- 使用真实值替换变量,再返回最终得到的字符串,这个过程称为渲染
为什么学模板?刚刚的示例代码,将所有的业务逻辑代码都写在一个py文件里面,公共的业务变更需要修改多处代码;掌握模板之后,就可以解决上述问题。
2.入门Flask模板
2.1.模板引擎Jinja2
模板引擎:可以简单理解为,它就是对模板(html)中的内容按照一定规则(变量)进行替换。得到最终我们给客户端展示的页面(模板)
- Flask使用Jinja2作为默认模板引擎,安装Flask的时候已经自动安装了;不需要再次安装Jinja2了
- 如果不是Flask框架,是其他框架想要使用模板引擎,也是可以安装的。通过pip进行安装:pip install Jinja2;或者通过源码安装:python setup.py install(把源码先下载下来,进入setup.py文件的目录)
Jinja2的默认配置
- template_folder='templates':这个是模板存放的默认目录,也可以自定义但是不建议改,就用它默认的就行了,便于项目成员理解共识
- render_template():(x)html自动转义,把html读取出来,也可以对它里面一些变量进行替换,在Jinja2里面这些变量的格式为双大括号开头,双大括号结尾,如:{{name}}
- render_template_string():字符串自动转义,html是以字符串赋值给一个变量的时候,可以用这个方法,可以将html响应结果展示出来;如果跟render_template一样传html文件名时,render_template_string是不会读取里面文件的,只会将这个文件名展示出来
-
{% autoescape %}:手动设置是否转义
- 全局函数和辅助对象:增强模板的功能
什么是转义?
把有特殊意义的字符显示出来,例如:
- html标签中的<>:<>
- 代码中的&:&
- 特殊字符转义对照表;详细的可以百度键入关键字“HTML特殊转义字符对照表”进行搜索
示例:
2.2.模板常用全局对象
- config:Flask的配置信息
- request:请求的对象
- session:会话对象
- g:请求相关的全局变量(如:g.user)
2.3.模板常用全局函数
- url_for():URL解析函数(如:静态文件地址解析、链接跳转地址解析);传入视图函数,显示其url路径;
常用场景:html常常会进行href页面跳转,如果直接指定路由(即路径),当视图函数路由发生变更时,就会找不到。实际开发中路由地址会变,但是视图函数一般不会变,这时候我们就可以使用url_for对视图函数进行反向解析获取其路由,这样html页面就能正常跳转了
????这么写,是常用手法
- get_flashed_messages():会话消息;常见场景退出登录,提示你下次再来;具体使用后面笔记会有,也可自行百度了解下,比如https://www.cnblogs.com/cangqinglang/p/13656739.html
2.4.模板上下文处理器
- 通过装饰器@app.context_processor实现
- 在模板的上下文中添加新的内容
- 内容可以是变量,也可以是函数
2.5.模板中变量的使用
思考:dict/list/tuple这些类型的数据怎么渲染?(开篇讲过,渲染:使用真实值替换变量,再返回最终得到的字符串展示,这个过程称为渲染)
模板当中变量的语法:{{ value }},双括号开头双大括号结尾;这个默认规则我们同样是可以在模板引擎配置中改成3个大括号.....都可以,建议不要动,使用 默认统一规范
- 简单数据类型的渲染,如字符串、整型、浮点型:{{ value }}
- dict类型数据的渲染:{{ object.attribute }}或{{ object['attribute'] }}
- list/tuple类型数据的渲染:{{ object[index] }}
- list/tuple嵌套dict复杂类型数据的渲染:详见示例代码
在使用render_template读取解析html模板时,将变量传递进去
1 # app.py 2 3 from flask import Flask, render_template 4 5 app = Flask(__name__) 6 7 @app.route('/index') 8 def index1(): 9 return 'hello zhangsan' 10 11 12 @app.route('/') 13 def index(): 14 # 1. 简单数据类型的渲染 15 age = 40 16 money = 65.89 17 name = '张三' 18 # 2. 用户信息 dict 19 user_info = { 20 'username': '张三', 21 'nickname': '三个', 22 'address.city': '广州', 23 'address.area': '天河' 24 } 25 # 3. 元组和列表 26 tuple_city = ('北京', '上海', '广州', '深圳') 27 list_city = ('北京', '上海', '广州', '深圳') 28 29 # 4. 复杂的数据结构 30 list_user = [ 31 { 32 'username': '张三', 33 'address': { 34 'city': '广州' 35 } 36 }, 37 { 38 'username': '李四', 39 'address': { 40 'city': '北京' 41 } 42 } 43 ] 44 return render_template('index.html', 45 age=age, 46 money=money, 47 name=name, 48 user_info=user_info, 49 tuple_city=tuple_city, 50 list_city=list_city, 51 list_user=list_user)
1 <!-- index.html --> 2 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <title>Title</title> 8 </head> 9 <body> 10 <h3>1. 简单数据类型的渲染</h3> 11 <p>我的年龄: {{ age }}</p> 12 <p>我的钱包: {{ money }}</p> 13 <p>我的名字:{{ name }}</p> 14 15 <h3>2. 用户信息 dict</h3> 16 <p>用户名: {{ user_info.username }}</p> 17 <p>用户昵称: {{ user_info.nickname }}</p> 18 <p>用户的地址:{{ user_info['address.city'] }}- {{ user_info['address.area'] }}</p> 19 20 <h3>3. 元组和列表</h3> 21 <p> 22 {{ tuple_city[0] }} <br/> 23 {{ tuple_city[1] }} <br/> 24 {{ tuple_city[2] }} <br/> 25 {{ tuple_city[3] }} <br/> 26 </p> 27 <p> 28 {{ list_city[0] }} <br/> 29 {{ list_city[1] }} <br/> 30 {{ list_city[2] }} <br/> 31 {{ list_city[3] }} <br/> 32 </p> 33 34 <h3>4. 复杂的数据结构</h3> 35 <p> 36 第一个:<br/> 37 用户名:{{ list_user[0].username }};地址: {{ list_user[0]['address']['city'] }} 38 </p> 39 <p> 40 第二个:<br/> 41 用户名:{{ list_user[1].username }};地址: {{ list_user[1]['address']['city'] }} 42 </p> 43 </body> 44 </html>
思考:长度为100的list对象如何渲染?
3.Flask模板语法
3.1.模板语法_模板标签
在【模板中变量的使用】笔记中,对list和tuple这两种python数据类型进行渲染的时候。发现长度非常长的时候,去取它的下标来进行渲染非常的不方便。模板标签就可以很好解决这个问题!
什么是模板标签?我们能在模板中,也就是html中写python的逻辑代码(条件判断、循环、赋值)吗?
模板标签语法:
1):{% tag %}
2):第二种是有tag开始和结束,一一对应的,比如写python逻辑代码if,在{% if %}开始,但是它不知道什么时候结束。最后用{% endif %}结尾,它就知道结束了
{% tag %}
内容
{% endtag %}
- 模板标签示例模板:条件表达式
<!-- 条件表达式 --> {% if condition_a %} 满足了A条件 {% elif condition_b %} 满足了B条件 {% else %} 都不满足 {% endif %}
- if标签中的is判断,以及一些其他的内置判断条件
- defined/undefined:变量是否已经定义
- none:变量是否为None
- number/string:数字/字符串判断
- even/odd:奇偶判断
- upper/lower:大小写判断
{% if value is defined %}
.......
{% endif %}
- if 标签中除了is还有其他逻辑控制判断
- and , or
- == , !=
- > , <
- >= , <=
- in , not in
- for循环,示例:如果{% for....... %}循环成立,显示for循环下面的语句块;如果for循环里面没有任何东西就会显示{% else %}下面的语句块
示例代码:
1 # app.py 2 3 from flask import Flask, render_template 4 5 app = Flask(__name__) 6 7 @app.route('/tag') 8 def tag(): 9 """ 模板标签的使用 """ 10 var = None 11 a = 2 12 list_user = [ 13 {'username': '张三', 'age': 32, 'address': '北京'}, 14 {'username': '李四', 'age': 22} 15 ] 16 # list_user = [] 17 return render_template('tag.html', 18 var=var, 19 a=a, 20 list_user=list_user)
1 <!-- tag.html --> 2 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <title>模板标签的使用</title> 8 </head> 9 <body> 10 <h3>if 的使用</h3> 11 {% if var is none %} 12 <p>var 是 none</p> 13 {% else %} 14 <p>var 不是 none</p> 15 {% endif %} 16 17 {% if a is defined %} 18 <p>a定义了</p> 19 {% else %} 20 <p> a 没有定义</p> 21 {% endif %} 22 {% if a == 2 %} 23 <p>a 是 2</p> 24 {% endif %} 25 26 <h3>for循环的使用</h3> 27 {% for item in list_user %} 28 <p>用户名:{{ item.username }},年龄:{{ item.age }}</p> 29 {% else %} 30 <p>用户信息为空</p> 31 {% endfor %} 32 </body> 33 </html>
- for循环体内的变量
思考:如果要在for循环中使用continue/break怎么办?
解决方案: jinja_env = Environment(extensions=['jinja2.ext.loopcontrols']) 下图这个扩展方式为覆盖,用的=。不建议,推荐使用添加扩展,下面的示例代码会有;同时 jinja2还有一些其他的扩展,感兴趣点击看下;
示例代码
1 # app.py 2 3 from flask import Flask, render_template 4 5 6 app = Flask(__name__) 7 # 添加扩展:为模板引擎添加扩展,支持break/continue语法 8 app.jinja_env.add_extension('jinja2.ext.loopcontrols') 9 10 ''' 11 该扩展为覆盖不建议使用 12 from jinja2 import Environment 13 app.jinja_env=Environment(extensions=['jinja2.ext.loopcontrols']) 14 ''' 15 @app.route('/tag') 16 def tag(): 17 """ 模板标签的使用 """ 18 var = None 19 a = 2 20 list_user = [ 21 {'username': '张三', 'age': 32, 'address': '北京'}, 22 {'username': '李四', 'age': 22}, 23 {'username': '王五', 'age': 32, 'address': '北京'}, 24 {'username': '王文', 'age': 22} 25 ] 26 # list_user = [] 27 return render_template('tag.html', 28 var=var, 29 a=a, 30 list_user=list_user)
1 <!-- tag.html --> 2 3 <!DOCTYPE html> 4 <html lang="en"> 5 <head> 6 <meta charset="UTF-8"> 7 <title>模板标签的使用</title> 8 <style type="text/css"> 9 .odd { 10 background-color: #f00; 11 color: #fff; 12 } 13 .even { 14 background-color: aqua; 15 color: #fff; 16 } 17 </style> 18 </head> 19 <body> 20 <h3>for 循环 dict</h3> 21 {% for user in list_user %} 22 <p class="{{ loop.cycle('odd','even') }}"> 23 第{{ loop.index }}个用户,总共{{ loop.length }}个:<br/> 24 {% for key, value in user.items() %} 25 {{ key }}: {{ value }} 26 {% endfor %} 27 </p> 28 {% else %} 29 <p>用户信息为空</p> 30 {% endfor %} 31 32 <h3>for 循环 dict -break</h3> 33 {% for user in list_user -%} 34 <p class="{{ loop.cycle('odd','even') }}"> 35 第{{ loop.index }}个用户,总共{{ loop.length }}个:<br/> 36 {% for key, value in user.items() -%} 37 {% if loop.index > 2 -%} <!--跳出了address的显示--> 38 {% break -%} 39 {% endif -%} 40 {{ key }}: {{ value }} 41 {% endfor -%} 42 </p> 43 {% else %} 44 <p>用户信息为空</p> 45 {% endfor %} 46 </body> 47 </html>
同时模板标签还支持添加注释,注释分为两种:
1)会在浏览器html源码中显示: <!-- 我是HTML注释 -->
2)不会在浏览器html源码中显示: {# 注释内容 #}
去除HTML源码中多余的空白可以提高性能,不能忽视!去除方式,在块的开始或结束放置一个减号(-),不能有空格
- 设置变量,进行赋值操作:先设置,后使用,可以通过import导入;
<!-- 设置变量 --> {% set key,value = (1,2) %}
建议设置变量的时候,和with代码块结合使用,实现块级作用域。这个变量只在with这个代码块中有效果。
思考:如下内容如何显示?
{{}} {% %}
1)视为字符串: {{ ‘{{}} {% %}’ }}
2)使用raw标签进行转义,使得特殊符号正常显示
3.2.模板语法_过滤器
思考:下面的场景如何实现?
什么是过滤器?
- 过滤器:定义好规则,修改变量(如:格式化显示)
- 过滤器的使用格式,用管道符号(|)分割,如:{{ name|striptags }},striptags就是过滤器,将name变量传递给striptags处理最后返回处理后的结果
- 可以链式调用:{{ name|striptags|title }},name传递给多个过滤器
- 可以用园括号传递可选参数{{ list|join(',')}}
过滤器的使用
方式一:用管道符( | )
{{ value|safe }}
方式二:使用便签{% filter 过滤器 %} {% endfilter %}
{% filter upper %}
This text becomes uppercase
{% endfilter %}
过滤器upper对字符串This text。。。进行格式化显示
内置的过滤器
- 求绝对值abs:{{ value|abs }}
- 默认值显示:default(value,default_value='',boolean=False)
- {{ value|default('默认值')}}:如果value变量没有定义,传递过来,default过滤器处理value返回“默认值”
- {{ value|d('默认值') }}:上面的简写
- html转义escape或e:{{ value | escape }} 或 {{ value | e }}
????由于浏览器一般默认对html自动转义成字符串输出,所以我们使用{% autoescape fale %}将其关闭。这时调试,过滤器e的作用就直观了。
- 富文本内容转义显示safe:{{ value|safe }}
- 倒叙显示reverse:{{ value|reverse }}
- 查看更多内置过滤器
自定义过滤器
方式一:使用装饰器@app.template_filter对自定义装饰器进行注册;推荐使用这个
- 示例reverse:自定义装饰器名称
- 示例reverse_filter函数:自定义装饰器的逻辑方法,需要传递被修改的变量s
@app.template_filter('reverse')
def reverse_filter(s):
return s[::-1]
方式二:调用函数app.jinja_env.filters对自定义装饰器进行注册
- 示例reverse:自定义装饰器名称
- 示例reverse_filter函数:自定义装饰器的逻辑方法,需要传递被修改的变量s
def reverse_filter(s):
return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter
3.3.模板语法_模板全局函数
思考:如何在模板中实现range函数的效果?
全局函数可在模板中直接使用
1 <ul> 2 {% for i in range(10) %} 3 <li>{{ i }}</li> 4 {% endfor %} 5 </ul>
模板提供的全局函数有:
- range([start],stop[,step]):和python的range用法基本一致
- dict(**items):转换成字典形式
- cycler(*items):常用于css类名的循环。
应用场景简单说明:声明变量class_name,通过cycler()赋值给这个变量。标签的class属性值等于这个变量,通过next()获取下一个。使用css查找class并对其样式修改。
- joiner(sep= ','):可用于字符串拼接;相当于python里面join的语法
- url_for():URL解析函数(如:静态文件地址解析、链接跳转地址解析)
静态文件地址解析;使用link引用外部css文件时,需要加上rel=stylesheet;rel="stylesheet" 描述了当前页面与href所指定文档的关系.即说明的是:href连接的文档是一个样式表。
链接跳转地址解析
4.Flask模板中的宏
什么是宏?
把常用功能抽取出来,实现可重用;简单理解:宏≈函数;宏可以写在单独的html文件中。
4.1.模板中的宏
1)定义宏{% macro %}:像书写函数一样定义宏,示例解析
- 示例代码定义了一个input的宏
- 调用input宏的时候,需要传入name、value、type、size;其中name是必传参数,value、type和size都有默认值,不传时使用默认值
- 返回的内容为????,调用input宏传递的参数,赋值给返回内容{{变量}}。其中value还经过了过滤器e进行了html转义。
<input type="{{ type }}" name="{{ name }}" value"{{ value|e }}" size="{{ size }}">