【Flask学习笔记:五】Jinja2 模板引擎

目录:
  【Flask学习笔记:一】开发环境部署
  【Flask学习笔记:二】Flask 入门基础知识
  【Flask学习笔记:三】Flask 中的 request、response
  【Flask学习笔记:四】Flask 应用和请求上下文
  【Flask学习笔记:五】Jinja2 模板引擎
  【Flask学习笔记:六】Flask 蓝图
  【Flask学习笔记:七】Flask - WTF 处理表单
  【Flask学习笔记:八】Flask 中的 cookie、session
  【Flask学习笔记:九】Flask-SQLAlchemy

【Flask学习笔记:五】Jinja2 模板引擎

  在 Flask 中通常使用 Jinja2 模板引擎来实现复杂的页面渲染。Jinja2 被认为是灵活、快速和安全的模板引擎技术,被广泛使用。Jinja2 的设计思想来源于 Django 模板引擎,它功能强大、速度快,并且提供了可选的沙箱模板执行环境安全保证机制,具有沙箱中执行、强大的 HTML 转义系统、模板继承等诸多优点。

1.模板引擎的一个简单应用

文件结构:
/static
  /style.css
/templates
  /login.html
  /success.html
/demo.py

demo.py 文件代码如下:

from flask import Flask, render_template, request, url_for
from werkzeug.utils import redirect

app = Flask(__name__)

app.config['DEBUG'] = True


@app.route('/')
def index():
    return redirect(url_for('login'))


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('password')
        return render_template('success.html', username=username, password=password)


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

login.html 代码如下:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
head>
<body>
    <div class="container">
        <span>登录span>
        {# 添加的注释 #}
        <form action="{{ url_for('login') }}" method="post">
            <p>username: <input type="text" name="username" />p>
            <p>password: <input type="password" name="password" />p>
            <input type="submit" value="Submit" />
        form>
    div>
body>
html>

success.html 代码如下:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    登陆成功,欢迎 {{ username }}
body>
html>

  Flask 通过 render_template() 函数来实现模板的渲染。要使用 Jinja 2 模板引擎,需要使用 from flask import render_template 命令导入 render_template 函数。在视图函数的 return 方法中, render_template() 函数第一个参数是指定模板文件的名称,比如这里的 login.html 和 success.html。

  render_template() 函数的第二个参数为可选项,可以为空。比如 login() 视图函数中方法为 GET 请求的 render_template(‘login.html’)。当然也可以传递第二个参数,比如例子中的 render_template(‘success.html’, username=username, password=password),一般是以键值对方式传递的。

  在模板中接受变量值,需要把变量放在 {{}} 中,比如 {{ username }}。模板中如果要写注释,格式为{# #},比如这里的 {# 添加的注释 #}。如果视图函数中有多个变量值都需要传递给模板,可以使用 **locals() 方法,改写之前的 login() 函数:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('password')
        return render_template('success.html', **locals())

在 render_template() 函数中,如果要给模板传递传递全部的本地变量,可以使用**local() 方法,此时,在模块中可以直接使用{{ username }}来直接使用变量。

2.模板中的控制语句 if 和 for

  在 Jinja 2 模板引擎中也可以使用 if 和 for 循环控制语句,控制模板的渲染。模板引擎中,if 和 for 语句应该放到 {% %}中。

控制语句 if:
if 语句的使用方式:

{% if 条件1 %}
	do something
{% elif 条件2 %}
	do something
{% else %}
	do something
{% endif %}

控制语句 for:

for 循环语句是 Python 中的一个循环控制语句,任何有序的序列对象内的元素都可以遍历,比如字符串、列表、元组等可迭代对象。模板中 for 循环的使用方式:

{% for 目标 in 对象 %}
	<p>目标</p>
{% endfor %}

实例:

from flask import Flask, render_template

app = Flask(__name__)

app.config['DEBUG'] = True


@app.route('/')
def index():
    dict_res = {
        'a': 'a',
        'b': 'b',
        'c': 'c'
    }
    list_res = [1, 2, 3]
    return render_template('index.html', **locals())


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

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    {% for key, value in dict_res.items() %}
        <p>{{ key }} : {{ value }}p>
    {% endfor %}

   {% for value in list_res %}
        <p>{{ value }}p>
    {% endfor %}
body>
html>

输出结果为:

a : a
b : b
c : c
1
2
3

Jinja 2 中 for 循环内特殊的变量有:

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

在 for 循环中,可以使用特殊的 loop.cycle 辅助函数,伴随循环在一个字符串/变量列表中周期取值:

{% for row in rows %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}

注意: 与 Python 中不同,不可以使用 continue 和 break 表达式来控制循环的执行。

3.自定义错误页面

  如果你在浏览器的地址栏中输入了无效的路由,会看到一个状态码为 404 的错误页面。这样的页面显的比较简陋,Flask 允许应用使用模板自定义错误页面。最常见的错误代码有两个: 404,客户端请求未知页面或路由时显示;500,应用有未处理的异常时显示。用 app.errorhandler 装饰器为这两个错误提供自定义的处理函数。

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404
    
@app.errorhandler(500)
def internal_server_error(error):
    return render_template('internal_server_error.html'), 500

4. Flask 的过滤器

常见过滤器:
  过滤器本质上是一个转换函数,有时候我们不仅需要输出变量的值,还需要把某个变量的值修改后再显示出来,而在模板中不能直接调用 Python 中的某些方法, 这就需要用到过滤器,过滤器与变量用管道符号( | )分割。
这里列举一个常用的过滤器:

  • {{ ’ ’ | default(‘the string was empty’, true) }}
    如果要对求值为false的变量使用default,则必须将第二个参数设置为 true

更多内置过滤器介绍详见 文档

自定义过滤器:
  当内置的过滤器满足不了我们的需求时,我们其实可以写出属于自己的过滤器。通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:

from flask import Flask, render_template

app = Flask(__name__)

app.config['DEBUG'] = True


@app.route('/')
def index():
    list_res = [1, 2, 3]
    return render_template('index.html', **locals())


def do_index_class(index): # 实现自定义过滤器,对第一个数加9
    if index == 1:
        return index + 9
    else:
        return index


app.add_template_filter(do_index_class, 'index_class')


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

使用自定义过滤器:

   {% for value in list_res %}
        <p>{{ value | index_class }}</p>
    {% endfor %}

打印结果:

10
2
3

5.宏的定义与使用

  Jinja 2 中的宏功能有些类似于传统程序语言中的函数,它跟 Python 中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来作为一个变量。

5.1.宏的定义

  宏有声名和调用两个部分。定义宏要加 macro,宏定义结束要加 endmacro 标志。举个例子:

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

	
    <p>用户名:{{ input('username') }}p>
    <p>密码:{{ input('password', type='password') }}p>
    <p>登录:{{ input('submit', type='submit', value='登录') }}p>

在这个例子中,宏的名称就是 input, 它有 3 个参数,分别是name、type 和 value,后两个参数有默认值。还可以在宏中加入 size 和 placeholder 等属性。
【Flask学习笔记:五】Jinja2 模板引擎_第1张图片
在宏内部,你可以访问三个特殊的变量:

名称 说明
varargs 如果有多于宏接受的参数个数的位置参数被传入,它们会作为列表的值保存在 varargs变量上
kwargs 同 varargs ,但只针对关键字参数。所有未使用的关键字参数会存储在 这个特殊变量中
caller 如果宏通过 call 标签调用,调用者会作为可调用的宏被存储在这个变量中

宏也可以暴露某些内部细节。下面的宏对象属性是可用的:

名称 说明
name 宏的名称
arguments 一个宏接受的参数名的元组
defaults 默认值的元组
catch_kwargs 如果宏接受额外的关键字参数(也就是访问特殊的 kwargs 变量),则为 true
catch_varargs 如果宏接受额外的位置参数(也就是访问特殊的 varargs 变量),则为 true
caller 如果宏访问特殊的 caller 变量且由 call 标签调用,则为 true

例:

 {% macro input(name, type, value) %}
     <input type="{{ type }}" name="{{ name }}" value="{{ value }}" />
      <br /> varargs : {{ varargs }}
      <br /> kwargs : {{ kwargs }}
      <br /> name : {{ input.name }}
      <br /> arguments : {{ input.arguments }}
      <br /> defaults : {{ input.defaults }}
      <br /> caller : {{ input.caller }}
      <br /> catch_kwargs : {{ input.catch_kwargs }}
      <br /> catch_varargs : {{ input.catch_varargs }}
  {% endmacro %}
  <p>用户名:{{ input('username','text','text','varargs1', 'varargs2', kwargs1='12345') }}p>

结果为:
【Flask学习笔记:五】Jinja2 模板引擎_第2张图片
在某些情况下,需要把一个宏传递到另一个宏。为此,可以使用特殊的 call 块。 下面的例子展示了如何让宏利用调用功能:

{% macro render_dialog(title, class='dialog') -%}
    <div class="{{ class }}">
        <h2>{{ title }}h2>
        <div class="contents">
            {{ caller() }}
        div>
    div>
{%- endmacro %}

{% call render_dialog('Hello World') %}
    This is a simple dialog rendered by using a macro and
    a call block.
{% endcall %}

5.2.宏的导入

  一个宏可以被不同的模板使用,所以建议将其声明在一个单独的模板文件中。需要使用时导入即可,而导入的方法类似于 Python 中的 import 。可以使用两种方式导入:

 {% from 'forms.html' import input as input_field %} # 第一种方式
 
 {% import 'forms.html' as forms %} # 第二种方式

单独定义宏的文件forms.html:

{% macro input(name, value='', type='text') -%}
    <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}

index.html:

 {% import 'forms.html' as forms %}

 <p>用户名:{{ forms.input('username') }}p>
 <p>密码:{{ forms.input('password', type='password') }}p>
 <p>登录:{{ forms.input('submit', type='submit', value='登录') }}p>

注意: 如果一个宏的名称以下划线开始,表示它是私有的,不能被导入

5.3. include 的使用

   include 语句可以把一个模板引入到另一个模板中,类似于把一个模板的代码复制到另一个模板的指定位置。

例:

header.html代码:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    <div>
        header
    div>
body>
html>

footer.html代码:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    <div>
        footer
    div>
body>
html>

index.html代码:


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
head>
<body>
    {% include "header.html" %}
    <div>
        网页内容
    div>
    {% include "footer.html" %}
body>
html>

显示效果:
【Flask学习笔记:五】Jinja2 模板引擎_第3张图片
注意: 使用 include 标签时是在 templates 目录中找寻相应的文件,不要使用相对路径。

6. set 和 with 语句的使用

  set 和 with 语句都可以在 Jinja 2 中定义变量并赋值。 set 定义的变量在整个模板范围内都有效, with 关键字在定义变量并赋值的同时,限制了 with 定义变量的作用范围,在这个作用域中的变量对外部是不可见的。

{% with %}
    {% set foo = 42 %}
    {{ foo }}           foo is 42 here
{% endwith %}
foo 不可访问

7.模板的继承

  Jinja 2 中最强大的部分就是模板继承,一个页面中都有标题、内容显示、底部等几个部分。我们可以将其中相同的部分提取出来,做为一个基础模板 base.html,其它网页就可以通过继承基础模板来显示相同的内容。模板的继承语法为:

{% extends '模板名称' %}

定义一个基模板 base.html:


<html lang="en">
<head>
    {% block head %}
    <title>{% block title %}super title{% endblock title %}title>
    {% endblock head %}
head>
<body>
    <div>{% block content %}super content{% endblock content %}div>
    <div>
        {% block footer %}
            super footer
        {% endblock footer %}
    div>
body>
html>

子模版 index.html 继承基模板:

{% extends 'base.html' %}

{% block title %}
    {{ super() }}
    subclass title
{% endblock title %}

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

{% block footer %}
    super footer
    

{{ self.content() }}

{% endblock footer %}

结果显示为:
【Flask学习笔记:五】Jinja2 模板引擎_第4张图片
  默认情况下,子模版如果实现了父模板定义的 block,那么子模版 block 中的代码就会覆盖父模板中的代码。如果想要在子模版中仍然保持父模板中的代码,那么可以使用 {{ super() }} 来实现,如例子中的 title 块,如果想要在一个 block 中调用其他 block 中的代码,可以通过 {{ self.block名称() }} 实现,如例子中的 footer 块复用 content 块代码。
  此外,Jinja 2 还允许你在块的结束标签中加入名称来改善代码的可读性,endblock 后面的名称一定与块名称相同。

8.使用 for 语句打印九九乘法表

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% for i in range(1,10) %}
        {% for j in range(1,i+1) %}
            {{ j }} * {{ i }} = {{ i*j }} |
        {% endfor %}
        <br />
    {% endfor %}
</body>
</html>

结果:
【Flask学习笔记:五】Jinja2 模板引擎_第5张图片

你可能感兴趣的:(Flask)