木心

毕竟几人真得鹿,不知终日梦为鱼

导航

flask框架--模板引擎Jinja2

1、使用 flask 中的 render_template 来渲染模板
2、字符串过滤器
3、列表过滤器
4、自定义过滤器
5、引入表单扩展Flask-WTF
6、模板宏的使用
7、父模板,模板继承和包含
8、闪现 flash的使用

1、使用 flask 中的 render_template 来渲染模板    <--返回目录

# coding:utf-8
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/index", methods = ["get", "post"])
def index():
    # return render_template("test.html", name="xxx", age=10)
    data = {
        "name": "yyy",
        "age": 20,
        "my_dict": {"city": "北京"},
        "my_list": [1, 2, 3],
        "my_int": 0
    }
    return render_template("test.html", **data)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, debug=True)

 

  模板文件 test.html 放在项目根目录的templates文件夹下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p>name = {{ name }}</p>
    <p>age = {{ age }}</p>

    <p>my_dict['city'] = {{ my_dict['city'] }}</p>
    <p>my_dict['city'] = {{ my_dict.city }}</p>

    <p>my_list[my_int] = {{ my_list[my_int] }}</p>

    <p>my_list[0] + my_list[1] = {{ my_list[0] + my_list[1] }} </p>
    <p> {{ "good good " + "study"}}</p>
</body>
</html>

 

2、字符串过滤器    <--返回目录

  过滤器用法:{{ " good good study " | trim }}

safe: 禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>
<p>{{ '<script>alert(123)</script>' | safe }}</p>  这里会执行js脚本代码

format: 格式化输出
<p>{{ '%s is %d' | format('zs', 20)}}</p>

capitalize: 把变量值的首字母转成大写,其余字母转小写
lower: 转成小写
upper: 转成大写
title: 把值中的每个单词的首字母都转成大写
trim: 去除首尾空格
reverse: 字符串反转

  支持链式使用过滤器: {{ " good good study " | trim | title }}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p>{{ '<script>alert("xss attack")</script>' | safe }}</p>
    <p>{{ '%s is %d' | format('zs', 20)}}</p>
    <p>{{ " good good study " | trim | title }}</p>
</body>
</html>

 

3、列表过滤器    <--返回目录

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p>取第一个元素: {{ my_list | first }}</p>
    <p>取最后一个元素: {{ my_list | last }}</p>
    <p>my_list 的长度: {{ my_list | length }}</p>
    <p>my_list 求和: {{ my_list | sum }}</p>
    <p>my_list 排序: {{ my_list | sort }}</p>
</body>
</html>

 

4、自定义过滤器    <--返回目录

  自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。

  方式1:通过app对象注册过滤器函数

# coding:utf-8
from flask import Flask, render_template

app = Flask(__name__)

# 定义过滤器
def list_step_2(list):
    """自定义的过滤器"""
    return list[::2]
# 注册过滤器
app.add_template_filter(list_step_2, "li2") # 第二个参数:在模板中显示使用的名称

@app.route("/index", methods = ["get", "post"])
def index():
    data = {
        "my_list": [1, 2, 3, 4],
    }
    return render_template("test.html", **data)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p>my_list = {{ my_list }}</p>
    <p>my_list | li2 = {{ my_list | li2 }}</p>
</body>
</html>

  方式2:装饰器 @app.template_filter("li3")

# coding:utf-8
from flask import Flask, render_template

app = Flask(__name__)

# 定义过滤器
@app.template_filter("li3")
def list_step_3(li):
    """自定义的过滤器"""
    return li[::3]

@app.route("/index", methods = ["get", "post"])
def index():
    data = {
        "my_list": [1, 2, 3, 4],
    }
    return render_template("test.html", **data)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, debug=True)

 

5、引入表单扩展Flask-WTF    <--返回目录

  使用Flask-WTF表单扩展,可以帮助进行SSRF验证,帮助我们快速定义表单模板,而且可以帮助我们在视图中验证表单数据。使用Flask-WTF需要配置参数SECRET_KEY。CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密钥生成加密令牌。

  WTForms支持的HTML标准字段

字段                      说明
StringField              文本字段
TextAreaField            多行文本字段
PasswordField            密码文本字段
HiddenField              隐藏文本字段
RadioField               一组单选框
SelectField              下拉列表
FileField                 文件上传字段
。。。

  WTForms常用验证函数

验证函数                  说明
DataRequired            确保字段中有数据
EqualTo                 比较两个字段的值,常用于比较两次密码输入
Length                  验证输入的字符串长度
NumberRange             验证输入的值再数字范围内
URL                     验证URL
AnyOf                   验证输入值再可选列表中
NoneOf                  验证输入值不在可选列表中

  安装

pip install Flask-WTF

  创建表单模型类

# coding:utf-8
from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo

app = Flask(__name__)
app.config["SECRET_KEY"] = "TEST_SECRET_KEY"

# 定义表单模型类
class RegisterForm(FlaskForm):
    """自定义的注册表单模型类"""
    # DataRequired 验证器:保证数据必填,并且不能为空
    user_name = StringField(label="用户名", validators=[DataRequired("用户名不能为空")])
    password = PasswordField(label="密码", validators=[DataRequired("密码不能为空")])
    password2 = PasswordField(label="确定密码", validators=[DataRequired("确定密码不能为空"), EqualTo("password", "两次密码不一致")])
    submit = SubmitField(label="提交")

@app.route("/register", methods = ["get", "post"])
def register():
    # 创建表单对象。如果时post请求,前端发送了数据,flask会把数据在构造form对象的时候,存放到对象中
    registerForm = RegisterForm()

    # 判断form中的数据是否合理。如果form中的数据完全满足所有的验证器,则返回真,否则返回假
    if registerForm.validate_on_submit():
        # 验证通过
        user_name = registerForm.user_name.data
        password = registerForm.password.data
        print("验证通过, user_name: {0}, password: {1}".format(user_name, password))
        return redirect(url_for("index"))
    
    return render_template("test.html", registerForm = registerForm)

@app.route("/index")
def index():
    return "index page"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, debug=True)

  模板使用表单模型类

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <form method="post">
        {{ registerForm.csrf_token }}
        <!-- 用户名 -->
        {{ registerForm.user_name.label }}:
        {{ registerForm.user_name }}  

        <!-- 错误提示信息 -->
        {% for msg in registerForm.user_name.errors %}
            {{ msg }}
        {% endfor %}
        <br/>
        
        <!-- 密码 -->
        {{ registerForm.password.label }}:
        {{ registerForm.password }}
        {% for msg in registerForm.password.errors %}
            {{ msg }}
        {% endfor %}
        <br/>

        <!-- 确定密码 -->
        {{ registerForm.password2.label }}:
        {{ registerForm.password2 }}
        {% for msg in registerForm.password2.errors %}
            {{ msg }}
        {% endfor %}
        <br/>

        <!-- 提交 -->
        {{ registerForm.submit.label }}:
        {{ registerForm.submit }}

    </form>
</body>
</html>

 

6、模板宏的使用    <--返回目录

  类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。

  不带参数宏的定义和使用

<!-- 不带参数宏的定义 -->
{% macro input() %}
    <input type="text" name="username" value=""/>
{% endmacro %}

<!-- 宏的使用 -->
{{ input() }}<br/>
{{ input() }}

  带参数宏的定义和使用

<!-- 带参数宏的定义 -->
{% macro input(type, name, size) %}
    <input type="{{text}}" name="{{name}}" value="" size="{{size}}"/>
{% endmacro %}
<!-- 宏的使用 -->
{{ input("text", "username", "80") }}

  

  宏定义在外部(跨文件,宏单独一个文件),取名为macro_input.html,放到templates目录下

<!-- 带参数宏的定义 -->
{% macro input(type, name, size) %}
    <input type="{{text}}" name="{{name}}" value="" size="{{size}}"/>
{% endmacro %}

  先导入宏文件,再使用

<!-- 导入宏文件 -->
{% import "macro_input.html" as m_input %}
<!-- 宏的使用 -->
{{ m_input.input("text", "username", "50") }}

 

7、父模板,模板继承和包含    <--返回目录

7.1、模板继承

  模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不不需要重复书写。

  {% block top %}  {% endblock %} 标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时可以进行填充。

  子模板使用 extends 指令声明这个模板继承自哪里,父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()。

   父模板 base.html

{% block top %}
顶部
{% endblock top %}

{% block content %}
{% endblock content %}

{% block bottom %}
底部
{% endblock bottom %}

  子模板

{% extends 'base.html' %}

{% block content %}
子模板填充的content
{% endblock content %}

  模板继承使用时注意:

    - 不支持多继承

    - 为了便于阅读,在子模板中使用entends时,尽量写在模板的第一行

    - 不能在一个模板文件中定义多个相同名字的block标签

 7.2、模板包含

  模板包含include: 将另一个模板整个加载到当前模板中,并直接渲染。使用方法 {% include 'nav.html' %}

  包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上ignore missing关键字,此时如果包含的模板文件不存在,会忽略这条include语句。{% include 'nav.html' ignore missing %}

7.3、宏、继承、包含 总结

  - 均能实现代码的复用

  - 继承的本质时代码替换,一般用来实现多个页面中重复不变的区域

  - 宏的功能类似函数,可以传入参数,需要定义、调用

  - 包含是直接将目标模板文件整个渲染出来

 

8、闪现 flash的使用    <--返回目录

  闪现 flash:一次session会话只有使用一次,使用完即清空。数据会存储在session中,默认session使用cookie进行存储。

@app.route("/index")
def index():
    global flag
    if flag:
        # 添加闪现信息
        flash("day")
        flash("day")
        flash("up")
        flag = False
    return render_template("flash.html")

  模板页面中使用 get_flashed_messages() 获取闪现信息

{% for msg in get_flashed_messages() %}
    <p>{{msg}}</p>
{% endfor %}

posted on 2022-02-14 17:02  wenbin_ouyang  阅读(244)  评论(0编辑  收藏  举报