Web后端学习笔记Flask(2)模板
模板渲染:
在flask中,视图函数可以直接通过render_template进行模板渲染。在flask中,模板文件是存放在template文件夹中:在调用模板文件的时候,模板文件的路径从template文件夹之后开始写,必须写完整的路径,在渲染模板的时候,默认是从项目的templates文件夹查找模板。
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hell Wold!'
@app.route('/index/')
def index():
return render_template("index/index.html")
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
如果不想将模板文件放到template文件夹下,则可以修改Flask默认的模板文件路径,进入Flask的定义文件,可以看到Flask的初始化的默认参数,在初始化Flask的时候修改对应的路径即可:
模板传参及其技巧:
在模板渲染的时候,同时也需要将后台的数据渲染到前台。
后端的代码:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hell Wold!'
@app.route('/index/')
def index():
return render_template("index/index.html", username="Knight-boy", age=18, country="China") # 传递参数
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 200px;
height: 200px;
border: 1px solid blanchedalmond;
background: burlywood;
}
</style>
</head>
<body>
<div class="container">
<h4>用户名: {{ username }}</h4>
<h4>年龄: {{ age }}</h4>
<h4>国别: {{ country }}</h4>
</div>
</body>
</html>
渲染的结果:
如果需要传入的参数较多,可以将参数写入到字典中,在进行传参
后台代码:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hell Wold!'
@app.route('/index/')
def index():
info = {
"username": "knight",
"age": 33,
"country": "China"
}
return render_template("index/index.html", info=info) # 这里参数名字与变量名相同即可
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 200px;
height: 200px;
border: 1px solid blanchedalmond;
background: burlywood;
}
</style>
</head>
<body>
<div class="container">
<h4>用户名: {{ info["username"] }}</h4>
<h4>年龄: {{ info["age"] }}</h4>
<h4>国别: {{ info["country"] }}</h4>
</div>
</body>
</html>
还有一种写法,在前端代码中,如果不需要通过字典名获取变量,可以在后端渲染页面传入参数的时候,将字典转换成关键字参数即可,(这种方法使用较多)
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def hello_world():
return 'Hell Wold!'
@app.route('/index/')
def index():
info = {
"username": "knight-b",
"age": 20,
"country": "China"
}
return render_template("index/index.html", **info)
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 200px;
height: 200px;
border: 1px solid blanchedalmond;
background: burlywood;
}
</style>
</head>
<body>
<div class="container">
<h4>用户名: {{ username }}</h4>
<h4>年龄: {{ age }}</h4>
<h4>国别: {{ country }}</h4>
</div>
</body>
</html>
模板中使用URL_FOR
在模板中也会使用url_for,例如在首页中,一般会有一个跳转按钮,再点击跳转按钮之后,会跳转到登录界面
{{ 适合存放变量 }}
{% 适合执行逻辑代码 %}
代码实现:在html代码中,可以使用url_for获取视图函数对应的url
后端代码:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def index():
return render_template("HTML/index.html")
@app.route('/login/')
def login():
return render_template("HTML/login.html")
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
首页代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 200px;
height: 150px;
border: 1px solid blanchedalmond;
background: burlywood;
}
</style>
</head>
<body>
<div class="container">
<a href="{{ url_for("login") }}">登陆</a>
</div>
</body>
</html>
登录页代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
<style>
.container{
width: 250px;
height: 170px;
border: 1px solid black;
background: aquamarine;
margin: 20px auto;
}
.container .username
{
display: block;
margin: 0 auto;
margin-top: 15px;
}
.container .password{
display: block;
margin: 0 auto;
margin-top: 15px;
}
.container .sub{
display: block;
margin: 0 auto;
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container">
<input type="text" placeholder="用户名" class="username">
<input type="password" placeholder="密码" class="password">
<input type="submit" class="sub">
</div>
</body>
</html>
过滤器基本使用:
过滤器是通过管道符号(|)进行使用的,例如{{ name|length }},将返回name的长度。过滤器相当于一个函数,把当前的变量传入到过滤器中,过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面。jingja2中内置了很多的过滤器,常用到的过滤器有:
1. abs(value), -1 | abs
2. first(value): 返回一个序列的第一个值 value | first
3. last(value): 返回最后一个值 value | last
更多的可以参考jingja2的手册:
后端代码:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def index():
return render_template("HTML/index.html", position=-6)
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 200px;
height: 150px;
border: 1px solid blanchedalmond;
background: burlywood;
}
</style>
</head>
<body>
<div class="container">
<span>The position is : {{ position|abs }}</span>
</div>
</body>
</html>
default过滤器详解:
对于传入的变量,如果有值,就返回变量的值,否则就返回default值,再在过滤器中,设置boolean参数为真,才可以将空字符串,空列表,null,None当作false来处理,否则即使有这些值,也会当作有值来处理。
escape过滤器:
转义字符,会将< >等符号转义成html中的符号。例如: content | escape, jingja2中的转义是默认开启的。
safe过滤器:
关闭字符串的自动转义 ,功能与{%autoescape off%} , {%endautoescape%}的类似
first / last过滤器:
获取序列的第一个或者最后一个元素
format过滤器:
用于格式化输出字符串 {{ “%s -- %s" }} | format(str1, str2)
length过滤器:
获取序列的长度
join过滤器:
将一个序列用特定的符号进行拼接
int, float, string过滤器:(用于模板的条件渲染)
对数值进行转换
lower, upper过滤器:
进行大小写转换
wordcount过滤器:
计算一个字符串中的单词数量
replace过滤器
实现字符串中的替换功能,replace(oldstr, newstr)
truncate过滤器:
截取一定长度的字符串,truncate(value, length, killwords=false), 后面的内容使用...表示,用于当显示内容较多时,显示缩略的内容。
stringtags过滤器:
删除字符串中所有的html标签,如果出现多个空格,将替换成一个空格。
后端代码:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
@app.route('/')
def index():
info = {
"position": -6,
"signature": None,
"content": '<script>alert("Hell")</script>',
"persons": ["tom", "lily", "Cart"],
"age": "14",
"text": "This is aa cc aa"
}
return render_template("HTML/index.html", **info)
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 300px;
height: 250px;
border: 1px solid blanchedalmond;
background: burlywood;
}
.container span{
display: block;
}
</style>
</head>
<body>
<div class="container">
<span>The position is : {{ position|abs }}</span>
<span>The signature is : {{ signature|default('no signature at all', boolean=True) }}</span>
<span>The content is: {{ content|escape }}</span>
<span>The content is: {{ content|safe }}</span> <!--关闭了自动转义,会弹出模态对话框-->
<span>First person is {{ persons|first }}</span>
<span>Last person is {{ persons|last }}</span>
<sapn>format use: {{ "%s -- %s" | format("hello", "cute")}}</sapn>
<span>Persons number is: {{ persons|length }}</span>
{% if age|int >= 18 %} <!--模板的条件选择渲染-->
<span>The age is ok</span>
{% else %}
<span>The age is not ok</span>
{% endif %}
<span>Replace result: {{ text|replace("aa", "***") }}</span>
<span>Truncated text is: {{ text|truncate(length=5) }}</span>
</div>
</body>
</html>
自定义过滤器:
在Jingja2内置的过滤器不能满足需求的时候,需要使用自定义的过滤器:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
app.config["TEMPLATES_AUTO_RELOAD"] = True # 保证模板修改后能够自动加载
@app.route('/')
def index():
info = {
"text": "This is aa cc aa"
}
return render_template("HTML/index.html", **info)
@app.template_filter("cut_xx") # 注册过滤器的名称
def cut_xx(string, newstr, oldstr):
"""
自定义过滤器
:param string:
:param newstr:
:param oldstr:
:return:
"""
value = string.replace(oldstr, newstr)
return value
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
自定义时间处理过滤器:
from flask import Flask, render_template
from datetime import datetime
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
app.config["TEMPLATES_AUTO_RELOAD"] = True # 保证模板修改后能够自动加载
@app.route('/')
def index():
info = {
"text": "This is aa cc aa",
"content": None,
"create_time": datetime(2020, 4, 5, 2, 12, 22)
}
return render_template("HTML/index.html", **info)
@app.template_filter("cut_xx") # 注册过滤器的名称
def cut_xx(string, newstr, oldstr):
"""
自定义过滤器
:param string:
:param newstr:
:param oldstr:
:return:
"""
value = string.replace(oldstr, newstr)
return value
@app.template_filter("handle_time")
def handle_time(time):
"""
发表时间距离现在时间
1. 一分钟以内,显示刚刚
2. 一小时以内,显示xx分钟
3. 24小时以内,显示xx小时
4. 大于二十四小时,显示xx天前
:param time:
:return:
"""
if isinstance(time, datetime):
time_span = datetime.now() - time
seconds = time_span.total_seconds()
if seconds < 60:
return "刚刚"
elif seconds < 60 * 60:
return "%d分钟前" % (seconds/60)
elif seconds < 24 * 60 * 60:
return "%d小时以前" % (seconds/60/60)
else:
return "%d天以前" % (seconds/24/60/60)
else:
return time
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.container{
width: 300px;
height: 250px;
border: 1px solid blanchedalmond;
background: burlywood;
}
.container span{
display: block;
}
</style>
</head>
<body>
<div class="container">
<span>The time is: {{ create_time|handle_time }}</span>
</div>
</body>
</html>
实现效果:
jingja2模板中条件判断,循环:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
.right{
color: #7666c1;
}
.wrong{
color: #19a71f;
}
</style>
</head>
<body>
{% if username=="tom" %}
<p class="right">hello, this is tom</p>
{% else %}
<p class="wrong">This is not tom</p>
{% endif %}
</body>
</html>
for循环可以遍历任何一个序列,包括列表,字典,元组,并且可以进行反向遍历:
但是jingja模板中,不支持用continue和break来控制循环,且for循环中还提供了以下的变量,用来获取遍历的状态:
loop.index 当前迭代的索引(从1开始)
loop.index0 当前迭代的索引(从0开始)
loop.first 是否是第一次迭代,返回bool值
loop.last 是否是最后一次迭代,返回bool值
loop.length 序列的长度
jingja2中的for循环可以由else,如果序列为空的话,会进入else语句:
后端代码:
from flask import Flask, render_template
app = Flask(__name__)
app.config.from_pyfile("config.py", silent=False) # 加载配置文件config, 也可以加载普通的txt文件
app.config["TEMPLATES_AUTO_RELOAD"] = True # 保证模板修改后能够自动加载
@app.route('/')
def index():
info = {
"username": "kit",
"persons": ["kit", "hack", "tom", "nancy"],
"books": [
{"name": "gone with wind",
"author": "unknown",
"price": 120,
"count": 35},
{"name": "Pride and Prejudice",
"author": "Jane Austing",
"price": 30,
"count": 120},
{"name": "Become Jane",
"author": "Jane Austing",
"price": 670,
"count": 40}
]
}
return render_template("HTML/index.html", **info)
if __name__ == '__main__':
app.run(debug=True) # 开启debug模式
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
*{
margin: 0;
padding: 0;
}
.person{
width: 200px;
border: 1px solid #fcffe8;
margin-bottom: 20px;
overflow: hidden;
}
.person p{
font-size: 18px;
color: #783476;
font-weight: bold;
display: block;
background: aqua;
}
.person .info
{
width: 100%;
line-height: 20px;
font-size: 16px;
color: #123;
background: #917e46;
}
table
{
border: 1px solid black;
text-align: center;
}
table td, th{
border: 1px solid black;
}
</style>
</head>
<body>
{% for person in persons %}
<div class="person">
<p>Hello, I am {{ person }}</p>
<div class="info">
coco
</div>
</div>
{% else %}
<div class="person">
<p>Hello, no person</p>
<div class="info">
no one is here
</div>
</div>
{% endfor %}
<table>
<thead>
<tr>
<th>index</th>
<th>book name</th>
<th>author</th>
<th>price</th>
<th>count</th>
</tr>
</thead>
<tbody>
{% for book in books %}
{% if loop.first %}
<tr style="background: red"> <!--将第一行设置为背景颜色为红色-->
{% elif loop.last %}
<tr style="background: orange">
{% else %}
<tr>
{% endif %}
<td>{{ loop.index }}</td>
<td>{{ book["name"] }}</td>
<td>{{ book["author"] }}</td>
<td>{{ book["price"] }}</td>
<td>{{ book["count"] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
实际效果:
jinja2中的宏,基本概念及使用:
宏类似于python中的函数,当一段代码需要反复使用的时候,可以将其封装为函数,这样在需要使用的时候就可以进行调用。模板中的宏可以传递参数,但是不能有返回值,可以将经常使用的代码片段放到宏中,然后把一些不固定的值抽取出来当成变量。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
</style>
</head>
<body>
<!--定义宏-->
{% macro input(name, type="text", value="") %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
<!--使用宏-->
<div class="container">
<label>
<p>用户名</p>
{{ input("username") }}
</label>
<label>
<p>密码</p>
{{ input("password", "password") }}
</label>
{{ input("submit", "submit", "提交") }}
</div>
</body>
</html>
宏的导入和注意事项:
通常在开发中,会将宏进行分类,放到单独的文件中,所以在其他的文件中需要调用宏的时候,首先需要将宏导入到文件中,才能够调用。
导入宏的方式和python中导入包非常类似,import xxx, import xxx as yyy, from xxx import yyy
{% from "HTML/login_form.html" import input %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
</style>
</head>
<body>
<!--使用宏-->
<div class="container">
<label>
<p>用户名</p>
{{ input("username") }}
</label>
<label>
<p>密码</p>
{{ input("password", "password") }}
</label>
{{ input("submit", "submit", "提交!") }}
</div>
</body>
</html>
宏文件在其他文件夹中的时候,需要以templates文件夹为根目录,开始查找。不能通过相对路径查找。
如果需要在导入宏的时候,将当前一些模板的参数传给宏所在的模板,那么在导入的时候应该使用with context, 例如:
from xxx import yyy with context
include 标签使用:
1. include表示将另一个文件中的代码,复制粘贴到include的当前位置。(有点c++中inline函数的意思)
2. include完全可以直接调用父模板中的变量:
3. 在include文件的路径时,只需要按照templates根目录进行加载,不能使用相对路径。跟import的路径形式一样
例如,分别定义header.html以及footer.html
header.html:(html +css代码)
<style>
.nav{
width: 100%;
height: 40px;
overflow: hidden;
background: aquamarine;
}
.nav ul{
font-size: 18px;
color: black;
line-height: 40px;
}
.nav ul li:hover{
background: blue;
}
.nav ul li{
float: left;
margin-left: 15px;
}
</style>
<div class="nav">
<ul>
<li>首页</li>
<li>课程详情</li>
<li>视屏教程</li>
<li>关于我们~</li>
<li>{{ username }}</li>
</ul>
</div>
footer.html
<style>
.footer{
width: 100%;
height: 200px;
background: black;
color: white;
}
</style>
<div class="footer">
这是底部footer
</div>
例如,需要在index.html中include文件header.html以及footer.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<style>
*{
margin: 0;
padding: 0;
}
ul{
list-style: none;
}
a{
text-decoration: none;
}
.middle{
width: 100%;
height: 300px;
background: burlywood;
}
</style>
</head>
<body>
{% include "common/header.html" %}
<div class="middle">
中间部分
</div>
{% include "common/footer.html" %}
</body>
</html>
此时,如果需要在不同的页面中使用header.html和footer.html.只需要在页面中include这两个文件即可。
set,with语句以及模板中定义常量:
在模板中,应该如何定义常量:{% set username=" xxx" %}
还可以使用with语句定义变量,with语句有起始和结束的地方,所以在with语句块内,可以使用该变量,但是在with语句块以外,不能使用这一变量。
{% with username="hell" %}
可以使用变量username的语句块
{% endwith %}
<body>
{% set username="Tom" %} <!--定义常量-->
<p>用户名:{{ username }}</p>
{% with name="hell" %}
<p>This is user {{ name }}</p>
{% endwith %}
</body>
也可以定义一个空的with语句,在这个空的with语句中,可以使用set定义变量,定义的变量也只能在这个with语句块中使用:
{% with %}
{% set name="xxx" %}
可以使用变量username的语句块
{% endwith %}
jingja2中加载静态文件:
静态文件: css文件,js文件,字体文件,图片文件,在flask中,静态文件存储在static文件夹下,在flask中加载静态文件,文件路径需要使用url_for("static", filename="路径")进行加载,其中路径需要以static文件夹为根目录。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>访问首页</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<script rel="script" src="{{ url_for('static', filename='JS/index.js') }}"></script>
</head>
<body>
<div class="container">
加载静态文件
</div>
<div class="picture">
<img src="{{ url_for('static', filename='images/sunset.png') }}" alt="">
</div>
</body>
</html>
jingja2中模板继承:
1. 为什么需要模板继承:
模板继承可以把一些公用的代码单独抽取出来,放到父模板中,以后子模版直接继承就可以使用了,减少重复性的代码,修改起来也更加的简单。
2. 使用extends来指明继承的父模板,父模板的路径也是相对于templates文件夹下的绝对路径。
3. 在父模板中,只能实现一些公共的代码。这时候父模板应该有能力提供一个接口,让父模板来实现
实例:
父类的html代码:设计整个模板,header, footer是共用的部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{% endblock %}
</title>
<style>
.nav{
width: 100%;
height: 40px;
overflow: hidden;
background: aquamarine;
}
.nav ul{
font-size: 18px;
color: black;
line-height: 40px;
}
.nav ul li:hover{
background: blue;
}
.nav ul li{
float: left;
margin-left: 15px;
}
.footer{
width: 100%;
height: 200px;
background: black;
color: white;
}
</style>
</head>
<body>
<div class="nav">
<ul>
<li>首页</li>
<li>课程详情</li>
<li>视屏教程</li>
<li>关于我们~</li>
</ul>
</div>
{% block content %}
<!--相当于占位的功能,具体的内容在子模板中实现-->
{% endblock %}
<div class="footer">
这是底部footer
</div>
</body>
</html>
定义自类的代码,继承父类:
<!--首页实现,继承自模板-->
{% extends "common/base_template.html" %} <!--继承父类-->
{% block title %}
CMS系统
{% endblock %}
{% block content %}
<style>
*{
margin: 0;
padding: 0;
}
ul{
list-style: none;
}
.content-block{
width: 100%;
height: 200px;
background: purple;
margin-top: 20px;
}
.content-block span{
font-size: 18px;
color: #ab811e;
display: block;
}
.content-block .item{
width: 50%;
height: 100px;
margin-top: 20px;
z-index: 2;
}
</style>
<div class="content-block">
<span>This is content</span>
<div class="item"></div>
</div>
{% endblock %}
----------------------------------------------------------------------------------------------------------------------------------------