02-模板(过滤器,控制代码块)

学习目标

  • 能够写出 jinja2 中变量代码块和控制代码块的格式
  • 能够写出在模板中字典,列表的取值方式
  • 能够写出数组反转的自定义过滤器(使用1种方式即可)
  • 能够说出Flask中模板代码复用的三种方式
  • 能够使用代码实现模板继承的功能
  • 能够说出可以在模板中直接使用的 Flask 变量和函数
  • 能够使用 Flask-WTF 扩展实现注册表单
  • 能够说出 CSRF 攻击的原理

Jinja2模板引擎简介

模板

  在前面的示例中,视图函数的主要作用是生成请求的响应,这是最简单的请求。实际上,视图函数有两个作用:处理业务逻辑和返回响应内容。在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。本节学到的模板,它的作用即是承担视图函数的另一个作用,即返回响应内容。

  • 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
  • 使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”
  • Flask是使用 Jinja2 这个模板引擎来渲染模板

使用模板的好处:

  • 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
  • 而模板则取到视图函数的数据结果进行展示(视图展示方面)
  • 代码结构清晰,耦合度低

  

Jinja2:

  Jinja2:是Python下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,他是Flask内置的模板语言。

  模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。

渲染模板函数

  Flask提供的render_template函数封装了该模板引擎,render_template函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。

使用

<h1>{{ post.title }}</h1>

{{}}来表示变量名,这种{{}}语法叫做变量代码块

Jinja2模板中的变量代码块可以是任一Python类型或者对象,只要他能够被Python的str()方法转换为一个字符串就可以,比如,可以通过下面的方法显示一个字典或者列表中的某个元素。

{{your_dict['key']}}
{{your_list[0]}}

用{%%}定义控制代码块,可以实现一些语言层次的功能比如循环或者if语句。

{% if user %}
    {{ user }}
{% else %}
    hello!
<ul>
    {% for index in indexs %}
    <li> {{ index }} </li>
    {% endfor %}
</ul>

注释

使用{# #}进行注释,注释的内容不会再html中渲染出来。

{# {{ name }} #}

模板的使用

  在项目下,创建temolates文件夹,用于存放所有的模板文件,并在目录下创建一个html文件,temp_demo1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
我的模板html内容
</body>
</html>

设置templates文件夹属性以便能够在代码中有智能提示。

设置html中的模板语言,以便在html有智能提示。

创建视图函数,将模板内容进行渲染返回

from flask import Flask,render_template

app = Flask(__name__)


@app.route('/')
def hello_world():
    return render_template("temp_demo1.html")


if __name__ == '__main__':
    app.run()

代码中传入字符串,列表,字典到模板中

from flask import Flask,render_template

app = Flask(__name__)


@app.route('/')
def hello_world():
    #往模板中传入数据
    my_str = "hello lishuntao"
    my_int = 100
    my_arr = [1,4,6,7,8]
    my_dic = {
        "name":"lishuntao",
        "pwd":"123456",
    }
    #渲染模板
    return render_template("temp_demo1.html",my_str=my_str,
                           my_int=my_int,
                           my_arr=my_arr,
                           my_dic=my_dic)


if __name__ == '__main__':
    app.run(port=8005)

模板中的显示代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板</title>
</head>
<body>
{{ my_str }}
{{ my_int }}
{{ my_arr }}
{{ my_dic }}
</body>
</html>

相关运算,取值

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板</title>
</head>
<body>
<br/>{{ my_str }}
<br/>my_int + 10 的和为:{{ my_int + 10 }}
<br/>数组的第0个值+my_int:{{ my_arr[0]+my_int }}
<br/>数组的第0个值:{{ my_arr[0] }}
<br/>数组的第1个值:{{ my_arr.1 }}
<br/>my_dic中name值:{{ my_dic["name"] }}
<br/>my_dic中pwd值:{{ my_dic["pwd"] }}
</body>
</html>

 过滤器

  过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。

使用方式:

#过滤器的使用方式为:变量名 | 过滤器。

{{variable | filter_name(*args)}}

如果没有任何参数传给过滤器,则可以把括号省略掉:

{{variable | filter_name}}

链式调用:(在Jinja2中,过滤器是可以支持链式调用的,示例如下:)

{{ "hello world" | reverse | upper }}

常见内建过滤器

字符串操作:

safe:禁用转义

<p>{{ '<em>hello</em>' | safe }}</p>

capitalize:把变量值的首字母转成大写,其余字母转小写

<p>{{ 'hello' | capitalize }}</p>

lower:把值转小写

<p>{{ 'HELLO' | lower }}</p>

upper:把值转成大写

<p>{{ 'hello' | upper }}</p>

title:把值中的每个单词的首字母都转成大写

<p>{{ 'hello' | title }}</p>

reverse:字符串反转

<p>{{ 'olleh' | reverse }}</p>

format:格式化输出

<p>{{ '%s is %d' | format('name',17) }}</p>

striptags:渲染之前把值中所有的HTML标签都删掉

<p>{{ '<em>hello</em>' | striptags }}</p>

列表操作

first:取第一个元素

<p>{{ [1,2,3,4,5,6] | first }}</p>

last:取最后一个元素

<p>{{ [1,2,3,4,5,6] | last }}</p>

length:获取列表长度

<p>{{ [1,2,3,4,5,6] | length }}</p>

sum:列表求和

<p>{{ [1,2,3,4,5,6] | sum }}</p>

sort:列表排序

<p>{{ [6,2,3,1,5,4] | sort }}</p>

语句块过滤

{% filter upper %}
    #一大堆文字#
{% endfilter %}

自定义过滤器

过滤器的本质是函数。当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种实现方式:

  • 一种是通过Flask应用对象的 add_template_filter 方法
  • 通过装饰器来实现自定义过滤器

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

需求:添加列表反转的过滤器

方式一:

  通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:

#过滤器的本质势函数,那么写一个反转函数
def do_listreverse(li):
    # 通过原列表创建一个新列表(不改动原列表)
    temp_li = list(li)
    # 将新列表进行返转
    temp_li.reverse()
    #返回新列表
    return temp_li
#第一个参数是自己写的过滤器的函数,第二个是自己定义过滤器使用的名字
app.add_template_filter(do_listreverse,'lireverse')

方式二:

用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器的名称。

#用装饰器来自定义过滤器,参数是自定义过滤器名称
@app.template_filter('lireverse')
def do_listreverse(li):
    # 通过原列表创建一个新列表
    temp_li = list(li)
    # 将新列表进行返转
    temp_li.reverse()
    return temp_li

在html中使用该自定义过滤器:

<br/> my_arr 原内容:{{ my_arr }}
<br/> my_arr 反转:{{ my_arr | lireverse }}

控制代码块

主要有两个:

1 if/else if /else / endif
2 for / endfor

if语句

Jinja2 语法中的if语句跟 Python 中的 if 语句相似,后面的布尔值或返回布尔值的表达式将决定代码中的哪个流程会被执行:

{%if user.is_logged_in() %}
    <a href='/logout'>Logout</a>
{% else %}
    <a href='/login'>Login</a>
{% endif %}

过滤器还可以用在if语句中

{% if comments | length > 0 %}
    There are {{ comments | length }} comments
{% else %}
    There are no comments
{% endif %}

循环

我们可以在Jinja2中使用循环来迭代任何列表或者生成器函数

{% for post in posts %}
    <div>
        <h1>{{ post.title }}</h1>
        <p>{{ post.text | safe }}</p>
    </div>
{% endfor %}

  循环和if语句可以组合使用,以模拟 Python 循环中的 continue 功能,下面这个循环将只会渲染post.text不为None的那些post:

{% for post in posts if post.text %}
    <div>
        <h1>{{ post.title }}</h1>
        <p>{{ post.text | safe }}</p>
    </div>
{% endfor %}

在一个 for 循环块中你可以访问这些特殊的变量:

变量描述
loop.index 当前循环迭代的次数(从 1 开始)
loop.index0 当前循环迭代的次数(从 0 开始)
loop.revindex 到循环结束需要迭代的次数(从 1 开始)
loop.revindex0 到循环结束需要迭代的次数(从 0 开始)
loop.first 如果是第一次迭代,为 True 。
loop.last 如果是最后一次迭代,为 True 。
loop.length 序列中的项目数。
loop.cycle 在一串序列间期取值的辅助函数。见下面示例程序。

在循环内部,你可以使用一个叫做loop的特殊变量来获得关于for循环的一些信息

  • 比如:要是我们想知道当前被迭代的元素序号,并模拟Python中的enumerate函数做的事情,则可以使用loop变量的index属性,例如:
{% for post in posts%}
{{loop.index}}, {{post.title}}
{% endfor %}

cycle函数会在每次循环的时候,返回其参数中的下一个元素,可以拿上面的例子来说明:

{% for post in posts%}
{{loop.cycle('odd','even')}} {{post.title}}
{% endfor %}

输出结果:

odd Post Title
even Second Post

示例程序:

app.py:

from flask import Flask,render_template

app = Flask(__name__)


@app.route('/')
def hello_world():
    my_list = [
        {
            "id": 1,
            "value": "我爱工作"
        },
        {
            "id": 2,
            "value": "工作使人快乐"
        },
        {
            "id": 3,
            "value": "沉迷于工作无法自拔"
        },
        {
            "id": 4,
            "value": "日渐消瘦"
        },
        {
            "id": 5,
            "value": "爱学习,爱自己"
        }
    ]
    #渲染模板
    return render_template("temp_demo1.html",my_list=my_list,
                          )


if __name__ == '__main__':
    app.run(port=8005,debug=True)

模板代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板</title>
</head>
<body>
{# 只显示4行数据,背景颜色依次为:黄,绿,红,紫,总共是五行数据 #}
{% for item in my_list if item.id != 5 %}
    {% if loop.index == 1 %}
        <li style="background-color: orange">{{ item.value }}</li>
    {% elif loop.index == 2 %}
        <li style="background-color: green">{{ item.value }}</li>
    {% elif loop.index == 3 %}
        <li style="background-color: red">{{ item.value }}</li>
    {% else %}
        <li style="background-color: purple">{{ item.value }}</li>
    {% endif %}
{% endfor %}
</body>
</html>

原文地址:https://www.cnblogs.com/lishuntao/p/11687556.html