Bootstrap

Flask之jinja2

模板的介绍

网站如何向客户端发送一个漂亮的页面呢

  • htmlcssjs

  • 但是如果将这些字段都写到视图中,作为HttpResponse()的参数响应给客户端,将会有以下问题

  • 视图部分代码臃肿, 耦合度高

  • 这样定义的字符串是不会出任何效果和错误的

  • 效果无法及时查看.有错也不容易及时发现

解决问题

模板 Template

  • MVT设计模式中的T,Template

  • M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。

  • V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。

  • T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "<h3>Hell</h3><p>Flask</p>"


if __name__ == "__main__":
    app.run(debug=True)

模板的使用

在 Flask中,配套的模板是 Jinja2,Jinja2的作者也是Flask的作者。这个模板非常的强大,并且执行效率高。

使用步骤

  • 创建模板
  • 在应用同级目录下创建模板文件夹templates 文件夹名称固定写法
  • 在templates文件夹下, 创建应用同名文件夹. 例, Book
  • 在应用同名文件夹下创建网页模板文件. 例 :index.html
  • 设置模板查找路径
  • 模板处理数据

使用render_template()使用模板

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("01_index.html")


if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        h1 {
            background-color: red;
        }
        p {
            background-color: blue;
        }
    </style>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模板的使用</title>
</head>
<body>
    <h1>Hello模板的使用</h1>
    <p>这个是模板的使用案例</p>
</body>
</html>

模板传递参数

在使用render_template渲染模版的时候,可以传递关键字参数

from flask import Flask,request,render_template
from werkzeug.routing import BaseConverter

app = Flask(__name__)

@app.route("/")
def index1():
    return render_template("02_index.html",info="Flask模板传参")

@app.route("/index2")
def index2():
    return render_template("02_index.html",info="python中的Flask模板传参",args="换了一个变量名")

@app.route("/home")
def home():
    context = {
        "uname":"吕布",
        "age":18,
        "height":180,
        "arms":{"melee":"方天画戟","range":"弓箭","transport":"赤兔马"}
    }
    return render_template("02_index.html",**context)

if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模板传参</title>
</head>
<body>
    <h1>模板传参</h1>
    <p>这个是关于{{info}}的内容</p><hr>
    <p>这是换了一个效果:{{args}}</p><hr>
    <p>{{uname}}的年龄是{{age}}岁,身高是{{height}},他的近战武器是{{arms["melee"]}},远程武器是{{arms.get("range")}},他的坐骑是{{arms.transport}}</p>
</body>
</html>

模板中使用url_for

解析出该函数映射的路由地址

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/")
def index1():
    return render_template("03_index.html")

@app.route("/home/")
def index2():
    return render_template("03_index.html")

if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>url_for的使用</title>
</head>
<body>
    <h1>url_for的使用</h1><hr>
    <p>index1的路由是{{url_for("index1")}}</p><hr>
    <p>index2的路由是{{url_for("index2")}}</p>
</body>
</html>

过滤器

有时候我们想要在模版中对一些变量进行处理,那么就必须需要类似于Python中的函数一样,可以将这个值传到函数中,然后做一些操作。

在模版中,过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中

jinja2中内置了很多过滤器

过滤器名解释举例
abs(value)返回一个数值的绝对值-1 | abs
int(value)将值转换为int类型
float(value)将值转换为float类型
string(value)将变量转换成字符串
default(value,default_value,boolean=false)如果当前变量没有值,则会使用参数中的值来代替。如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换name|default(‘xiaotuo’)
safe(value)如果开启了全局转义,那么safe过滤器会将变量关掉转义content_html|safe
escape(value)或e转义字符,会将<、>等符号转义成HTML中的符号content|escape
first(value)返回一个序列的第一个元素
format(value,*arags,**kwargs)格式化字符串
last(value)返回一个序列的最后一个元素。

自定义过滤器

只有当系统提供的过滤器不符合需求后,才须自定义过滤器。

过滤器本质上就是一个函数。

如果在模版中调用这个过滤器,那么就会将这个变量的值作为第一个参数传给过滤器这个函数,

然后函数的返回值会作为这个过滤器的返回值。

需要使用到一个装饰器:@app.template_filter('过滤器名称')

from flask import Flask,render_template

app = Flask(__name__)

@app.template_filter("cut") # 自定义过滤器的名称
def cut(value):
    value = value.replace("你","我")
    return value

@app.route("/")
def index():
    info = "你好啊"
    return render_template("07_index.html",info = info)

if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>自定义过滤器</title>
</head>
<body>
    <h1>自定义过滤器</h1>
    过滤前的数据{{ info }}<br>
    过滤后的数据{{ info | cut }}<br>
</body>
</html>

自定义时间过滤器

操作发布新闻 与现在的时间间隔 **

from datetime import datetime
from flask import Flask,render_template

app = Flask(__name__)

"""实现类似朋友圈多少时间之前发布的信息"""
# 假设数据库中的数据是2020/01/01 03:10:10
# 现在的时间是2022/10/31 08:10:10
@app.template_filter("handler_time")
def handler_time(time):
    # 获取当前时间
    now = datetime.now()
    # 相差时间由秒为单位
    # total_seconds() 此方法的返回类型是一个数字,该数字是该时间段内覆盖的总秒数
    temp_stamp = (now - time).total_seconds()
    if temp_stamp < 60:
        return "1分钟之前"
    elif 60*60 > temp_stamp >= 60:
        return "1小时之前"
    elif 24*60*60>temp_stamp>=60*60:
        hours = temp_stamp/(60*60)
        return f"{int(hours)}小时之前"
    elif 24*60*60*30>=temp_stamp>=24*60*60:
        day = temp_stamp/(60*60*24)
        return f"{int(day)}天以前"
    else:
        return "很久以前"

@app.route("/")
def index():
    tmp_time = datetime(2022,10,31,10,10,19)
    return render_template("08_index.html",tmp_time = tmp_time)

if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>自定义时间过滤器的使用</title>
</head>
<body>
    <h1>自定义时间过滤器的使用</h1>
    数据过滤后:{{ tmp_time|handler_time}}
</body>
</html>

流程控制-选择结构

所有的控制语句都是放在{% ... %}中,并且有一个语句{% endxxx %}来进行结束

if:if语句和python中的类似,可以使用>,<,<=,>=,==,!=来进行判断,也可以通过and,or,not,()来进行逻辑合并操作

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/")
def index():
    uname = "李四"
    return render_template("09_index.html",uname = uname)

if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>流程控制——选择结构</title>
</head>
<body>
    <h1>流程控制——选择结构</h1>
    {% if uname == "张三"%}
        我是{{ uname }}
    {% else %}
        我不是张三,而是{{ uname }}
    {% endif %}
</body>
</html>

练习

模拟登录页面

from flask import Flask,render_template,request

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("10_index2.html")

@app.route("/login")
def login():
    user = request.args.get("user")
    return render_template("10_index2.html",user=user)


if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>选择结构</title>
</head>
<body>
    {% if not user %}
    <a href="/login?user='nick'">请登录</a>&nbsp;&nbsp;&nbsp;
    <span>免费注册</span>
    {% else %}
        <h1>欢迎 {{ user }} </h1>
        <h2>首页</h2>
    {% endif %}
</body>
</html>

流程控制-循环结构

实现九九乘法表

from flask import Flask,render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("11_index.html")


if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>九九乘法表</title>
</head>
<body>
    <table border="1">
        {% for i in range(1,10) %}
        <tr>
            {% for j in range(1,i+1) %}
            <td>{{j}}*{{i}} = {{i*j}}</td>
            {% endfor %}
        </tr>
        {% endfor %}
    </table>
</body>
</html>

宏的使用

模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值。

可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量。

定义宏

{% macro inp(type="text",name="",value="")%}
    <input type="{{type}}" name={{name}} value="{{value}}">
{% endmacro %}

使用宏

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>宏的使用</title>
</head>
{% import 'macros/common.html' as mc %}
{% from "macros/common.html" import inp with context %}
    <body>
        <h1>未使用宏</h1>
        <tr>
            <td>用户名:</td>
            <td><input type="text" value="{{nick}}"></input></td><br>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="" id=""></td><br>
        </tr>
        <tr>
            <td>登录:</td>
            <td><input type="submit" value="登录" name="" id=""></td>
        </tr>
    </body><hr>


    <body>
        <h1>宏的使用</h1>
        <tr>
            <td>用户名:</td>
            <td>{{inp(name="uname")}}</td><br>
        </tr>
        <tr>
            <td>密码:</td>
            <td>{{inp("password","pwd")}}</td><br>
        </tr>
        <tr>
            <td>登录:</td>
            <td>{{inp("submit",value="登录")}}</td>
        </tr>
    </body>
</html>
```language

include导入模板

页面的复用

  1. 这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位置。

  2. include标签,如果想要使用父模版中的变量,直接用就可以了,不需要使用with context

  3. include的路径,也是跟import一样,直接从templates根目录下去找,不要以相对路径去找。

;