Flask05:Jinja2

Flask 使用 Jinja2 作为默认模板引擎。你完全可以使用其它模板引擎。但是不管你 使用哪种模板引擎,都必须安装 Jinja2 。
使用Pycharm创建Flask项目时,可以指定模板引擎:
在这里插入图片描述
在Flask中,其模板默认的位置为templates目录。这一点在前面我们提到过。Jinja2模板只是一个文本文件。Jinja可以生成任何基于文本的格式(HTML,XML,CSV,LaTeX等)。一个Jinja2模板并不需要有一个特定扩展名:.html,.xml或任何其他扩展名都是可以的。

在 Flask 中, Jinja2 默认配置如下:

  • 当使用 render_template() 时,扩展名为 .html 、 .htm 、 .xml 和 .xhtml 的模板中开启自动转义。
  • 当使用 render_template_string() 时,字符串开启 自动转义。
  • 在模板中可以使用 {% autoescape %} 来手动设置是否转义。
  • Flask 在 Jinja2 环境中加入一些全局函数和辅助对象,以增强模板的功能。

一般来说我们会创建一个html文件,然后使用render_template函数返回改文件。看个简单例子:
在这里插入图片描述
在这里插入图片描述
代码很简单,这里就是返回一个html文件。

接下来我们简单聊一下Jinja2模板的基础使用。

概要
对于Jinja2模板来说,最重要的概念大概就是如何在模板文件中使用变量和表达式来达到我们动态渲染页面文件的目的。这一点对于所有的模板框架来说应该是一致的。就个人理解,模板其实很简单,就是字符串文本的替换工作,只不过不同的框架为我们提供了不同的实现方式,我们需要做的是使用符合模板框架规则的语法来编写我们自己的页面。

分隔符

默认的Jinja分隔符配置如下:

  • {% … %}对于声明
  • {{ … }}用于将表达式打印到模板输出
  • {# … #}用于模板输出中不包含的注释
变量

模板变量通过模板上下文中可用的变量来传递的。简单来说就是我们在返回渲染内容时同时将模板需要用到的参数一并传入。


def render_template(template_name_or_list, **context):
    """Renders a template from the template folder with the given
    context.

    :param template_name_or_list: the name of the template to be
                                  rendered, or an iterable with template names
                                  the first one existing will be rendered
    :param context: the variables that should be available in the
                    context of the template.
    """
    ctx = _app_ctx_stack.top
    ctx.app.update_template_context(context)
    return _render(
        ctx.app.jinja_env.get_or_select_template(template_name_or_list),
        context,
        ctx.app,
    )

Flask默认使用Jinja2模板,在Jinja2中可以使用如下的标准环境变量:

  • config:当前配置对象( flask.config )
  • request:当前请求对象( flask.request )。 在没有活动请求环境情况下渲染模板时,这个变量不可用。
  • session:当前会话对象( flask.session )。 在没有活动请求环境情况下渲染模板时,这个变量不可用。
  • g:请求绑定的全局变量( flask.g )。 在没有活动请求环境情况下渲染模板时,这个变量不可用。
  • url_for():flask.url_for() 函数。
  • get_flashed_messages():flask.get_flashed_messages() 函数。
@app.route('/')
def hello_world():
    params = {
        'single_variable': '我是单独定义的变量',
        'home': {
            'title': '首页',
            'navigation': []
        }
    }
    for rule in app.url_map.iter_rules():
        navigation = params.get('home').get('navigation')
        navigation.append({
            'rule': rule.rule,
            'methods': list(rule.methods),
            'endpoint': rule.endpoint
        })
    return render_template('index.html', **params)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ home.title }}</title>
</head>
<body>

{{ single_variable }}

<table border="1" bgcolor="#f0f8ff" width="80%">
    <caption><h1>后台接口API</h1></caption>
    <tr bgcolor="#ffd700" align="center">
        <th>路由地址</th>
        <th>请求方法</th>
        <th>端点</th>
    </tr>
    {# 这里会显示出所有的API接口 #}
    {% for item in home.navigation %}
        <tr align="center">
            <td>{{ item.rule }}</td>
            <td>
                {{ item.methods | join(",") }}
            </td>
            <td>{{ item.endpoint }}</td>
        </tr>
    {% endfor %}
</table>


<table border="1" bgcolor="#ffebcd" width="80%">
    {# 这里会显示出所有的API接口 #}
    <tr align="center">
        <td>开发模式</td>
        <td>{{ config.get("ENV") }}</td>
    </tr>
    <tr align="center">
        <td>是否开启DEBUG</td>
        <td>{{ config.get("DEBUG") }}</td>
    </tr>
    <tr align="center">
        <td>最大COOKIE</td>
        <td>{{ config.get("MAX_COOKIE_SIZE") }}</td>
    </tr>
    <tr align="center">
        <td>url_for示例</td>
        <td>{{ url_for('hello_world') }}</td>
    </tr>
    <tr align="center">
        <td>request示例</td>
        <td>{{ request.host_url }}</td>
    </tr>
</table>
</body>
</html>

看个效果:
在这里插入图片描述
关于变量的使用 我们只需要记着传入的变量在引用的时候需要使用双花括号{{...}}。在模板中变量名必须和我们传入的变量名称相同。

另外,如果我们有很多变量,但是又不想自己去组装一个字典格式的变量,那么我们可以使用python内置函数locals(),同样可以达到目的。

@app.route('/')
def hello_world():
    single_variable = '我是单独定义的变量'
    home = {
        'title': '首页',
        'navigation': []
    }
    for rule in app.url_map.iter_rules():
        navigation = home.get('navigation')
        navigation.append({
            'rule': rule.rule,
            'methods': list(rule.methods),
            'endpoint': rule.endpoint
        })
        # return render_template('index.html', **params)
    return render_template('index.html', **locals())

此时如果打印下locals()的值,你会发现locals() 函数会以字典类型返回当前位置的全部局部变量。
在这里插入图片描述

同样在官方文档中提到关于变量获取时的一个小的注意点:

  • {{ foo.bar }}
  • {{ foo[‘bar’] }}

这里列出两者在官方文档中给出的差异:

For the sake of convenience, foo.bar in Jinja does the following things on the Python layer: check for an attribute called bar on foo (getattr(foo, ‘bar’)).if there is not, check for an item ‘bar’ in foo (foo.getitem(‘bar’)).if there is not, return an undefined object.

foo[‘bar’] works mostly the same with a small difference in sequence: check for an item ‘bar’ in foo. (foo.getitem(‘bar’)). if there is not, check for an attribute called bar on foo. (getattr(foo, ‘bar’)). if there is not, return an undefined object.

更多的请大家查阅一下关于python中getitem和getattr的相关文章。

注释

要注释掉模板中一行的一部分,请使用默认设置为的注释语法。这对于注释掉模板的某些部分以进行调试或为其他模板设计者或您自己添加信息很有用:{# … #}。

{# note: commented-out template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}
模板中定义变量

在模板中,可以使用"set"语句来定义变量。语法很简单:

{% set 变量名=%}
{{ 变量名 }}

另外我们还可以通过with语句来定义一个局部的临时变量:

{% with classroom = "angle" %}
      <p>名字:{{ classroom }}</p>
{% endwith %}

此时classroom 变量只在with范围内有效。

过滤器

变量可以通过过滤器修改。过滤器由竖线符号(|)与变量分开,并且括号中可以包含可选参数。可以链接多个过滤器。一个滤波器的输出将应用于下一个。常见的过滤器有:

  • abs(value):返回一个数值的绝对值。例如:-1|abs。
  • default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。
    name|default(‘xiaotuo’) ==
    如果name不存在,则会使用xiaotuo来代替。boolean=False默认是在只
    有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递Boolean=true。也可以使用or来代替
  • escape(value)或e:转义字符,会将等符号转义成HTML中的符号。例如content|escape或content|e
  • first(value):返回一个序列的第一个元素。name|first
  • format(value,*args,**kwargs):格式化字符串。
  • last(value):返回一个序列的最后一个元素。示例:names|last。
  • length(value):返回一个序列或者字典的长度。示例:names|length
  • join(value,d=‘u’):将一个序列用d这个参数的值拼接成字符串
  • safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe
  • int(value):将值转换为int类型
  • float(value):将值转换为float类型
  • lower(value):将字符串转换为小写
  • upper(value):将字符串转换为大写
  • replace(value,old,new):替换将old替换为new的字符串
  • truncate(value,length=253,killwords=False):截取length长度的字符串
  • striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格
  • trim:截取字符串前面和后面的空白字符
  • string(value):将变量转换成字符串
  • wordcount(s):计算一个长字符串中单词的个数
 default过滤器

使用方式’’,如果value这个’key’不存在,那么就会使用’default’过滤器提供的默认值。

{{ home.title | default("http://localhost:5000/") }}
自动转义

自动转义是指自动对特殊字符进行转义。特殊字符是指 HTML ( 或 XML 和 XHTML ) 中的 & 、 > 、 < 、 " 和 ’ 。因为这些特殊字符代表了特 殊的意思,所以如果要在文本中使用它们就必须把它们替换为“实体”。如果不转义 ,那么用户就无法使用这些字符,而且还会带来安全问题。有时候,如需要直接把 HTML 植入页面的时候,可能会需要在模板中关闭自动转义功 能。这个可以直接植入的 HTML 一般来自安全的来源,例如一个把标记语言转换为 HTML 的 转换器。

有三种方法可以控制自动转义:

  1. 在 Python 代码中,可以在把 HTML 字符串传递给模板之前,用 Markup 对象封装。一般情况下推荐使用这个方法。
  2. 在模板中,使用 |safe 过滤器显式把一个字符串标记为安全的 HTML (例如: {{ myvariable|safe }} )。
  3. 临时关闭整个系统的自动转义。
@app.route('/')
def hello_world():
    html = Markup('Hello, <em>World</em>!')
    html2 = 'Hello, <em>World</em>!'
    return render_template('index.html', **locals())
<p style="background: blanchedalmond">使用Markup转义</p>
{{ html }}
<p style="background: blanchedalmond">不使用safe转义</p>
{{ html2 }}
<p style="background: blanchedalmond">使用safe转义</p>
{{ html2 | safe }}
<p style="background: blanchedalmond">使用autoescape不关闭自动转义</p>
{% autoescape false %}
<p>{{ html2 }}</p>
{% endautoescape %}
<p style="background: blanchedalmond">使用autoescape关闭自动转义</p>
{% autoescape true %}
<p>{{ html2 }}</p>
{% endautoescape %}

在这里插入图片描述
简单来说,就是看你项目的需求是否需要展示html代码的原始文本,如果需要展示原始文本,那么请关闭转义。

定义自己的过滤器

在开发项目的过程中,经常会遇到的情况就是原始框架未能提供符合我们实际情况的函数或者其他的机制,此时我们最喜欢的是去找一找有没有现成的轮子,没有的话那么只能试着自己去实现了。同样的我们可以定制自己的过滤器在Jinja2中使用,当然了,这里是基于Flask的。

在Flask中我们可以通过下面两种方式注册自己的过滤器:
1.使用jinja_env 手动注册过滤器

2.使用 template_filter() 装饰器

@app.template_filter('reverse_word')
def reverse_filter(s):
    return s[::-1]


def reverse_filter2(s):
    return s[::-1]


app.jinja_env.filters['reverse_word2'] = reverse_filter2

从本质上来看,过滤器其实就是一个函数,执行特定的功能,但是我们需要告知jinja2模板,这个过滤器是可用的,也就是这里提到的在flask中注册过滤器。

控制语句语法

1.if语句

{% if username is string %}
    输入的用户名是有效的字符串
{% elif username is number %}
    输入的用户名是有效的数字
{% else %}
    输入的用户名是不是字符串也不是数字
{% endif %}

 
  1. for 循环
{% for user in users %}
    <p>用户名称为:{{ user }}</p>
{% 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.depth指示当前渲染在递归循环中的深度。从1级开始
loop.depth0指示当前渲染在递归循环中的深度。从0级开始
loop.previtem循环的上一个迭代中的项目。在第一次迭代中未定义。
loop.nextitem循环的以下迭代中的项目。在上一次迭代期间未定义。
loop.changed(*val)如果以前使用其他值调用(或根本未调用),则为true。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>title</title>
</head>
<body>
{% for user in users %}
    <p>用户名称为:{{ user }}</p>
    <p>loop.index:{{ loop.index }}</p>
    <p>loop.index0:{{ loop.index0 }}</p>
    <p>loop.revindex:{{ loop.revindex }}</p>
    <p>loop.revindex0:{{ loop.revindex0 }}</p>
    <p>loop.first:{{ loop.first }}</p>
    <p>loop.last:{{ loop.last }}</p>
    <p>loop.length:{{ loop.length }}</p>
    <p>loop.depth:{{ loop.depth }}</p>
    <p>loop.depth0:{{ loop.depth0 }}</p>
    <p>loop.previtem:{{ loop.previtem }}</p>
    <p>loop.nextitem:{{ loop.nextitem }}</p>
{% endfor %}
</body>
</html>

在这里插入图片描述

模板继承

这里我们在说最后一个知识点吧,关于jinja2模板的知识还是需要大家自己去查阅相关的文档,这里这能抛砖引玉让大家有个大概的了解。
还有很多细微的知识点并没有提及。

模板的主要作用就是为了重用,这一点在很多编程语言都有类似的思想。就是为了少写一些无聊的重复的代码。

首先我们定义一个基础模板:

<html lang="en">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>
</html>

在此示例中,标记定义了子模板可以填充的四个块。所有块标记所做的只是告诉模板引擎子模板可以覆盖模板中的那些占位符。{% block %}。

看到这里我们也就清楚了,jinja2中我们需要使用{% block %}标记这里是一个待填充的区域,这是第一步,简单明了。

那么就应该定义具体填充的字模板了。

{% extends "base.html" %}
{% block title %}模板继承编程{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important {
            color: #336699;
        }
    </style>
{% endblock %}
{% block content %}
    <h1>模板继承编程</h1>
    <p class="important">
        Welcome to flask jinja2 program.
    </p>
{% endblock %}

先来运行下看看效果:
在这里插入图片描述

第一个关键的知识点,{% extends "......html" %}.这里我们使用该语法格式告诉模板引擎当前的文件扩展了另外一个模板,当模板引擎在处理该模板时会先查找我们指定的父模板文件。

同时我们在填充具体的block区域时,需要明确指定父模板中对应的block名称。

最后一点就是如果子模板没有实现对应的block,那么会使用父模板中定义的block内容。

写在最后

另外还有宏定义和文件的include两个知识点,这里就不做介绍了。有需要大家自己去查阅文档:https://jinja.palletsprojects.com/en/2.11.x/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值