模板
1-模板的简介
模板是一个web开发必备的模块。因为我们在渲染一个网页的时候,并不是只渲染一个纯文本字符串,二手需要渲染一个有富文本标签的页面。这时候我们就需要使用模板了。在Flask中,配套的模板是JinJa2,Jinja2的作者也是Flask的作者。这个模板非常强大,并且执行效率高。
Flask渲染Jinja模块
- 渲染模板使用的方法为
render_template
- 当使用该模板的时候,该模板会自动的去
templates
该文件夹目录下寻找该html
文件来作为模板 - 当访问
/about/
的时候,about()
函数会在当前目录下的templates
文件夹下寻找index.html
模板文件。如果想更改模板文件地址,应该在创建app的时候,给Flask传递一个关键字参数template_foloer
指定具体的路径
# -*- coding: utf-8 -*-
# @Time : 2020/4/9 16:54
# @Author : 大数据小J
# render_template:渲染模板
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
@app.route('/about/')
def about():
# end
return render_template('index.html')
if __name__ == '__main__':
app.run()
from flask import Flask,render_template
app = Flask(__name__,template_folder=r'C:\templates')
@app.route('/about/')
def about():
return render_template('about.html')
# -*- coding: utf-8 -*-
# @Time : 2020/4/9 16:54
# @Author : 大数据小J
# render_template:渲染模板
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
@app.route('/about/')
def about():
return render_template('index.html')
@app.route('/login/')
def lonin():
return render_template('login/login.html')
if __name__ == '__main__':
app.run(debug=True)
模板传参
- 如果只有一个或者少量参数,直接在
render_template
函数中添加关键字参数就可以了。 - 如果有多个参数的时候,那么可以先把所有的参数放在字典中,然后在
render_template
中, - 使用两个星号,把字典转换成关键字参数传递进去,这样的代码更方便管理和使用
- 在模板中,如果要使用一个变量,语法为
{{要打印的参数}}
- 访问模型中的属性或者字典,可以通过
{{params.property}}
形式,或则是使用{{params['age']}}
**context
在render_template
方法中会自动解析以字典形式的数据
# -*- coding: utf-8 -*-
# @Time : 2020/4/9 17:17
# @Author : 大数据小J
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
@app.route('/about/')
def about():
# return render_template('index.html', username='SmallJ', study={'Python', 'Java', 'PHP'},
# data={'name': 'SmallJ', 'age': 18, 'class': '三班'})
context = {
'username': 'SmallJ',
'age': 18,
'study': 'Python'
}
# **context相当于会把字典变成对应的关键字的形式
# username=SmallJ
return render_template('index.html', **context)
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
<h1>{{ username }}</h1>
{# <h1>{{ study }}</h1>} #}
<h1>{{ age }}</h1>
<h1>{{ study }}</h1>
</body>
</html>
2-Jinja2模板过滤器
Jinja2模板过滤器
过滤器是通过管道符号(|)进行使用的,例如:{{name|length}}
,将返回name的长度。过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。Jingja2中内置了许多过滤器,在这里可以看到所有的过滤器。
abs(value)
:返回一个数值的绝对值default(value,default_value,boolean=false)
:如果当前变量没有值,则会使用参数的值来代替name|defalut('SmallJ')
——如果name值不存在,则会使用SmallJ
来替换。boolean=False
默认是在只有这个变量为undefined
的时候才会使用default
中的值,如果想使用Python的形式判断是否为False
,则可以传递boolean=true
。也可以使用or
来替换escape(value)
:转义字符first(value)
:返回一个序列的第一个元素。formate(value,*arags,**kwargs)
:格式化字符串last(value)
:返回一个序列的最后一个元素。length(value)
:返回序列的长度join(value,d='要拼接的字符串')
:将一个序列用d这个参数的值拼接成字符串safe(value)
:如果开启了全局转义,那么safe过滤器会将变量关掉转义int(value)
:将值转换成int类型float(value)
:将值转换为float类型lower(value)
:将字符串转换成小写upper(value)
:将字符串转换成大写replace(value,old,new)
:替换将old替换为new的字符串truncate(value, length=255, killwords=False)
:截取length长度的字符串striptags(value)
:删除字符串中的所有的HTML标签,如果出现多给空格,将替换成一个空格。trim
:截取字符串前面和后面的空白字符(不包括中间的字符)sring(value)
:将变量转换成字符串wordcount(s)
:计算一个字符串中单词的个数(其实是按照空格来进行计算,也可以通过s来指定修改的内容)
自定义模板过滤
自定义模板使用的方法为template_filter()
括号里面传模板自定义使用的名字,在模板中生效使用
# -*- coding: utf-8 -*-
# @Time : 2020/4/10 23:45
# @Author : 大数据小J
# 导入模板文件: render_template
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
@app.route('/index/')
def index():
context = {
'username': 'SmallJ',
'age': -18, # 当我传入一个负数值的时候可以通过Jinja2模板中的abs绝对值来改变,返回项目上线的时候出现负数的形式
'class': '三班',
'Javascript': '<script>alert("hello");</script>',
# first返回第一个元素,当为列表的时候,返回Python。当为字符串的时候返回P
# last返回元素中最后一个元素值
'books': ['Python', 'Java', 'PHP'],
'data': {
'students': 'SmallJ',
'id': 9947,
'number': 'demodemo',
'Study': 'PHP'
},
'height': '174.5',
'label': '<a href="https://passport.baidu.com/v2/?login" class="account-login">登录</a>',
'content': ' No problem ',
'word_data': '分词结果'
}
return render_template('index.html', **context)
# 自定义模板过滤 ()括号中的值为自己在模板中自定义使用的名字
@app.template_filter('my_filter')
# 这里为什么要有个value呢,,就是你需要自己定义一个模板方法,没有value跟直接return没什么区别
def add(value):
return value.replace('SmallJ', 'SevenJ')
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{#{模板过滤器是通过管道符号(|)来执行的,过滤器相当于是函数,把当前的变量传入到过滤器当中,然后过滤器通过根据自己的功能在返回相应的结果
abs():为绝对值
default('要替换的值'):当传入变量的值不存在的时候,模板会选择default中的值来进行
safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义
first(value):返回序列中的第一个元素
last(value):返回一个序列中最后一个元素值
format(value, *arags, **kwargs):格式化字符串
length(value):返回一个序列或者字典的长度,长度从1开始
int(value):返回的结果为整型
float(value):返回的结果为浮点型
lower(value):将字符串转换为小写
upper(value):将字符串转换为大写
replace(value, old, new):将把ord替换为new的字符串
striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成为空格
trim:截取字符串前面和后面的空白字符(但不会去掉中间的空格)
string(value):将变量转换成字符串
wordcount(s):计算一个常字符串中单词的个数(wordcount分词是按照空格的形式来进行分词)
my_filter:对应着自己自定义的模板名字
}#}
<p>{{ username }}</p>
<p>{{ age|abs() }}</p>
<p>{{ name|default('SenvenJ') }}</p>
<p>{{ books|first() }}</p>
<p>{{ books|last() }}</p>
<p>{{ "%s"|format('StudyJinja2模板') }}</p>
<p>{{ books|first()|length() }}</p>
<p>{{ height|int() }}</p>
<p>{{ username|lower() }}</p>
<p>{{ username|upper() }}</p>
<p>{{ books|first()|replace('ython','YTHON') }}</p>
<p>{{ label|striptags() }}</p>
<p>{{ content|trim() }}</p>
<p>{{ height|string() }}</p>
<p>{{ word_data|wordcount() }}</p>
<p>{{ username|my_filter }}</p>
</body>
</html>
显示时间
# -*- coding: utf-8 -*-
# @Time : 2020/4/11 21:55
# @Author : 大数据小J
from flask import Flask, render_template
from datetime import datetime
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
# 建立模板文件
@app.route('/index/')
def index():
context = {
'username': 'SmallJ',
'datetime': datetime(2020, 4, 11, 22, 42, 00)
}
return render_template('filter.html', **context)
# 自定义模板过滤
@app.template_filter('my_filter')
def add(time):
"""
大于一分钟:显示刚刚
大于1分钟小于1小时:xx分钟之前
大于1小时小于24小时:显示xx小时之前
:param time:
:return:
"""
if isinstance(time, datetime):
now_time = datetime.now()
# total_seconds转换成秒数,两个时间戳进行相减
timestamp = (now_time-time).total_seconds()
if timestamp < 60:
return '刚刚'
elif timestamp >= 60 and timestamp <= 60*60:
return "{}分钟之前".format(int(timestamp/60))
elif timestamp >= 60*60 and timestamp<= 24*60*60:
return '{}小时之前'.format(int(timestamp/(60*24)))
else:
return time
if __name__ == '__main__':
app.run(debug=True, port=8888)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ username }}</h1>
<h1>{{ datetime|my_filter }}</h1>
</body>
</html>
3-控制语句
所有的控制语句都是放在{ %…% }中,并且有一个语句{ % endxxx %}来进行结束,Jinja中常用的控制语句有if for in
for...in...
:for循环可以遍历任何一个序列包括列表、字典、元组。并且可以进行反向遍历
# -*- coding: utf-8 -*-
# @Time : 2020/4/13 9:07
# @Author : 大数据小J
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World'
@app.route('/decide/')
def decide():
context = {
'username': 'SmallJ',
'study': ['Python', 'PHP', 'Java'],
'data': {
'username': 'SmallJ',
'age': 18,
'city': '广州'
}
}
return render_template('if_for.html', **context)
if __name__ == '__main__':
app.run(debug=True)
并且Jinja中的for循环还包括以下变量,可以用来获取当前的遍历状态。
loop.index
:返回当前索引(从1开始)。
loop.first
:判断当前是否是第一次迭代,返回 True 或者 False。
loop.last
:判断当前是否为最后一次迭代,返回True 或者 False。
loop,length
:返回序列的长度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
{# {hr为分割线
li为无序标点
loop.index:返回当前迭代的索引(从1开始)
loop.first:判断当前是否第一次迭代,返回True 或者 False
loop.last: 判断当前是否是最后一次迭代,返回True 或者 False
loop.length:返回序列的长度
} #}
<body>
{% if username == 'SmallJ' %}
<p>这是SmallJ</p>
{% else %}
<p>这不是SmallJ</p>
{% endif %}
{% for book in study %}
<li>{{ loop.last }}</li>
<li>{{ loop.index}}</li>
<li>{{ book }}</li>
{% endfor %}
{% for key, value in data.items() %}
<hr>
<p>{{ loop.first}}</p>
<p>{{ loop.length }}</p>
<p>{{ key }}</p>
<p>{{ value }}</p>
{% endfor %}
<hr>
</body>
</html>
4-宏和input
模板中的宏跟Python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
<p>用户名: {{ input('username') }}</p>
<p>密码: {{ input('password', type='password') }}</p>
<p>{{ input(value='提交',type='submit') }}</p>
</body>
</html>
# -*- coding: utf-8 -*-
# @Time : 2020/4/13 11:01
# @Author : 大数据小J
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return '欢迎来到Flask世界'
@app.route('/sud/')
def sud():
context = {
'username': 'SmallJ'
}
return render_template('macro.html', **context)
if __name__ == '__main__':
app.run(debug=True)
import语句
在真实的开发中,会将一些常用的宏单独放在一个文件中,在需要使用的时候,再从这个文件中进行导入。import语句的用法跟Python中的import类似,
-
导入方式一:
import ‘导入的html’ as 重命名
-
导入方式二:
import '导入的宏html' import 导入的模块
另外需要注意的是,导入的模块并不会把当前上下文的变量添加到被导入的模板中,如果你想要导入一个需要访问当前上下文变量的宏,有两种可能的方法。
- 显示地传入请求或请求对象的属性作为宏的参数
- 与上下文一起(
with context
)导入宏
macro.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
<!-- <p>用户名: {{ input('username') }}</p>-->
<!-- <p>密码: {{ input('password', type='password') }}</p>-->
<!-- <p>{{ input(value='提交',type='submit') }}</p>-->
</body>
</html>
导入宏文件macro_copy.html
{% import 'macro.html' as macro %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>用户名: {{ macro.input('username') }}</p>
<p>密码: {{ macro.input('password', type='password') }}</p>
<p>{{ macro.input(value='提交', type='submit') }}</p>
</body>
</html>
# -*- coding: utf-8 -*-
# @Time : 2020/4/13 11:01
# @Author : 大数据小J
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return '欢迎来到Flask世界'
@app.route('/sud/')
def sud():
context = {
'username': 'SmallJ'
}
return render_template('macro_copy.html', **context)
if __name__ == '__main__':
app.run(debug=True)
5-include和set语句
inclde语句
include
语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置
inclde的好处
- 代码复用性高
- 节省项目的开发时间
title.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p1>国内</p1>
<p1>国外</p1>
<p1>国际</p1>
<p1>军事</p1>
<p1>财经</p1>
<p1>娱乐</p1>
<p1>体育</p1>
<p1>互联网</p1>
</body>
</html>
botton.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p1>百度新闻独家作品</p1>
<p1>1. 新闻由机器选取每5分钟自动更新</p1>
<p1>2. 百度新闻搜索源于互联网新闻网站和频道,系统自动分类排序</p1>
<p1>3. 百度不刊登或转载任何完整的新闻内容</p1>
</body>
</html>
personal.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# {common为templates文件夹目录下的文件夹} #}
{% include 'common/title.html' %}
<p>这是中心内容</p>
{% include 'common/botton.html' %}
</body>
</html>
# -*- coding: utf-8 -*-
# @Time : 2020/4/13 11:59
# @Author : 大数据小J
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
@app.route('/filter/')
def filter():
return render_template('personal.html')
if __name__ == '__main__':
app.run(debug=True)
赋值(set)语句
有时候我们想在模板中添加变量,这很好赋值语句(set)就派上用场了{% set name='SmallJ' %}
当定义了set语句的时候,该值为全局变量的值,当代码和模板中同时出现name
值的时候,模板中的name
值高于代码中name
的值
赋值语句创建的变量在其之后都是有效的,如果不想让一个变量渲染全局环境,可以使用with
语句来创建语句一个内部的作用域,将set
语句放在其中,这样创建的变量只在with
代码中才有效。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# {set为全局变量} #}
{# {当我们想让set为局部变量的时候我们可以通过with 和 endwith 代码块中实现 } #}
{% set username='SmallJ' %}
{% include 'common/title.html' %}
<p>这是中心内容</p>
<p>{{ username}}</p>
<hr>
{% with %}
{% set name='这是局部的' %}
<h1>{{ name }}</h1>
{% endwith %}
{% include 'common/botton.html' %}
</body>
</html>
# -*- coding: utf-8 -*-
# @Time : 2020/4/13 11:59
# @Author : 大数据小J
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello_world'
@app.route('/filter/')
def filter():
context = {
'username': '大数据小J'
}
return render_template('personal.html', **context)
if __name__ == '__main__':
app.run(debug=True)
6-模板继承
为什么需要模板继承
模板继承可以把一些公用代码单独抽取出来放到一个父模板中,以后子模板直接继承就可以使用。这样重复的代码修改起来也会比较方便
模板继承的语法
使用{% extend '要继承的模板html' %}
block语法
一般在父模板中,定义一些公共的代码。子模板可能要根据具体的需求实现不同的代码。
这时候父模板就应该提供一个接口,让父模板来实现。从而实现具体业务需求的功能
在父模板中定义:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% block content %}
<li>国内</li>
<li>国外</li>
<li>军事</li>
<li>科技</li>
<li>生物</li>
<li>生活</li>
{% endblock %}
</ul>
{% block title %}
<h1>这是父模板中的首页</h1>
{% endblock %}
{% block footer %}
<div class="footer">
这是文章的底部内容
</div>
{% endblock %}
</body>
</html>
succde.html
{# {extends 中文意思为扩展} #}
{% extends 'index.html' %}
{% block title %}
<h1>这是子模板中的首页</h1>
{% endblock %}
调用父模板的代码block中的代码
默认情况下,子模板如果实现了父模板定义的block,那么子模板block中的代码就会覆盖掉父模板中的代码,如果想在子模板中仍然保持父模板代码,需要用{{ super() }}
函数调用
{# {extends 中文意思为扩展} #}
{% extends 'index.html' %}
{% block title %}
{{ super() }}
<h1>这是子模板中的首页</h1>
{% endblock %}
总结
-
子模板中的代码,第一行应该是继承的代码
{% extends '要继承的父模板.html' %}
-
子模板中,如果要实现自己的代码,应该放在
block
里面,如果放在外码,将无法被渲染 -
当子模板继承了父模板后,没有对父模板进行
block
修改的时候,默认情况下会全部继承子模板。 -
block
名字不能够重名,block
可以嵌套 -
在子模板中不能自己定义标签,但可以在父模板中定义标签。
静态文件的配置
静态资源文件一般会放在flask
项目下的static
文件目录下面。Web应用中会出现大量的静态文件来使得网页更加生动美观。类似于css样式文件、JavaScript脚本文件、图片文件、字体文件等静态资源。在Jinja中加载静态文件非常简单,只需要通过url_for
全局函数就可以实现了。
静态资源文件使用的是<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
- 静态资源文件使用的是
url_for
,第一个值默认为static
为样式文件,``filename`:为加载样式文件的目录 - 默认请求下,静态资源文件会去
static
文件下找相对应的文件 - JavaScrip和imags同理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<img src="{{ url_for('static',filename='images/index.png')}}" alt="">
</head>
<body>
<ul>
{% block content %}
<li>国内</li>
<li>国外</li>
<li>军事</li>
<li>科技</li>
<li>生物</li>
<li>生活</li>
{% endblock %}
</ul>
{% block title %}
<h1>这是父模板中的首页</h1>
{% endblock %}
{% block footer %}
<div class="footer">
这是文章的底部内容
</div>
{% endblock %}
</body>
</html>