首页 > 代码库 > 网站后端_Python+Flask.0014.FLASK模版相关之使用JinJa2模版渲染?

网站后端_Python+Flask.0014.FLASK模版相关之使用JinJa2模版渲染?

简单 说明:


说明:  之前章节中,视图函数直接返回文本,而实际生产环境很少这样,因为实际的页面大多是带有样式和复杂逻辑的HTML+CSS+JS代码,这可以让浏览器渲染出非常漂亮和复杂的效果,页面内容应该是可以重用的,而且需要更执行更高级的功能


自带 模版:


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://xmdevops.blog.51cto.com/
# Purpose:
#
"""
# 说明: 导入公共模块
from string import Template
# 说明: 导入其它模块
# 方式二: 自定义替换符以及匹配字符
class AtTemplate(Template):
    delimiter = r‘@‘
    idpattern = r‘[_a-z][_a-z0-9]*‘
if __name__ == ‘__main__‘:
    tp = Template(‘‘‘
    name: $name
    age : $age
    ‘‘‘)
    # 方式一: 默认替换符为$,匹配规则为[_a-z][_a-z0-9]*
    print tp.substitute(name=u‘李满满‘, age=24)
    print tp.safe_substitute(name=u‘刘珍珍‘, age=25)
    tp = AtTemplate(‘‘‘
    name: @name
    age : @age
    ‘‘‘)
    # 方式二: 自定义替换符为@匹配字符为[_a-z][_a-z0-9]*
    print tp.substitute(name=u‘李满满‘, age=24)
    print tp.safe_substitute(name=u‘刘珍珍‘, age=25)

说明: string.Template类,很简单的模版,支持有限,无法继承重用,这对于WEB开发远远不够,所以我们需要使用第三方专业模版系统,目前比较出名的是Jinja2和Mako模版系统


Jinja2模版:

1. 让页面逻辑独立于业务逻辑,开发的程序易于维护

2. 提供流程控制,继承等高级功能使得模版非常灵活,快速,安全


# API 使用


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://xmdevops.blog.51cto.com/
# Purpose:
#
"""
# 说明: 导入公共模块
from jinja2 import Template
# 说明: 导入其它模块
if __name__ == ‘__main__‘:
    # 说明: 渲染字符串
    template = Template(‘Hello {{ name }}!‘)
    print template.render(name=u‘李满满‘)

说明: 由于单独拿API来使用不太常用,如果单独拿API使用的话掌握如上写法即可,和string.Template类用法基本一致,至于其它渲染指定文件可用读取成字符串再渲染,有兴趣可学习官方API文档

# 强大模版

1. 模版支持任何基于文本的格式(HTML/XML/CSV/LaTex等等),并没有特定的扩展名

# 基础语法

{%- ... -%}

说明: 主要解析流程控制等

{#-  ...  -#}

说明: 主要添加代码注释等

{{    ...    }}

说明: 主要解析模版表达式

# 数据类型

说明: 模版中支持字符串,数值,列表,元组,字典, boolean(true/false,注意是小写的),除此之外还支持全局函数/内置过滤器|/内置测试器is/in关键字/if关键字/字符连接符~

1. 支持算术运算符,+,-,/,//,%,*,**,

2. 支持比较运算符,==,!=,>,>=,<,<=

3. 支持逻辑运算符,and,or,not,(expr)

4. 支持其它运算符,in,is,|,~(连接字符串使用{{ ‘hello ‘ ~ name ~ ‘!‘ }},如果name是limanman则返回hello limanman!),()调用可调用量,./[]获取对象属性

# 变量相关

1. 应用可将任意类型变量传递给模版,访问带属性变量既可以加.访问,也支持[]访问,两者的区别是查找的先后顺序,前者先加.测试,然后再[]测试,后者先[]测试然后在.测试,所以根据具体传递变量类型决定

2. 模版中中可以为变量赋值,在顶层的(块,宏,循环之外)赋值是可导出的,也就是说可以从别的模块儿中导入使用


{%- set navigation = [(‘index.html‘, ‘Index‘), (‘about.html‘, ‘About‘)] -%}
{%- set key, value =  call_something() -%}

# with语句

1. 模版中支持with语句,和PY内置with类似,with后面常跟表达式,主要用于限制对象的作用域


{% with arr=range(10) %}
    {% for i in arr %}
        {{ i }}
    {% endfor %}
{% endwith %}

# 过  滤 器

1. 过滤器和管道符|配合可链式修改变量,过滤器还支持传递参数,内置的过滤器列表可参考(http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters)

{{ v|abs }}

说明: 返回值的绝对值

{{ v|attr(name) }}

说明: 返回v对象的name属性值

{{ v|batch(linecount, fill_with=None) }}

说明: 将v序列linecount个元素分为一组,保证每组长度相同,如果不够用fill_with的值补充,常用于生成表格


<!--说明: 创建一个10行10列的表格,通过batch分割成10行,然后再次遍历生成10个td-->
<table width="600px" style="border-collapse: collapse;margin: 0 auto;" border="1">
    {%- for tr in table_tds|batch(10, "&nbsp;") -%}
        <tr>
            {%- for td in tr -%}
                <td>{{ td }}</td>
            {%- endfor -%}
        </tr>
    {%- endfor -%}
</table>

{{ v|capitalize }}

说明: 整个语句的首字母大写,其它字母小写

{{ v|default(default_value=http://www.mamicode.com/‘‘, boolean=False) }}

说明: boolean为False,只有未定义时才返回default_value,boolean为True时,只要v为False就使用default_value.

{{ v|dictsort(case_sensitive=False, by=‘key‘) }}

说明: 对v字典进行排序case_sensitive设置是否大小写敏感,by指定键排序

{{ v|escape }}

说明: 对v进行安全转义,可以简写为e

{{ v|filesizeformat(binary=False) }}

说明: 格式化v为可读的文件大小默认以字节为单位

{{ v|first }}

说明: 返回序列对象的第一个元素

{{ v|float(default=0.0) }}

说明:转换value值为float类型,如果转换失败则返回default的值

{{ v|forceescape }}

说明: 不管v是否被转义过,一律进行html转义,可能会二次转义

{{ v|format(*args, **kwargs) }}

说明: 使用*args和**kwargs去填充格式化字符串v

{{ v|groupby(attr) }}

说明: 对序列v中的对象/字典按照attr分组,分组后组名存储在group.grouper里,而组员保存在group.list,如下是一个站点地图的简单实现


<ul>
    {%- for group in persons|groupby("gender") -%}
        <li>{{ group.grouper }}</li>
        <ul>
            {%- for person in group.list -%}
                <li>{{ person[‘name‘] }}, {{ person[‘age‘] }}</li>
            {%- endfor -%}
        </ul>
    {%- endfor -%}
</ul>

{{ vint(default=0) }}

说明: 将v的值转换为int如果转换失败返回default的值

{{ v|join(d=‘‘, attribute=None) }}

说明: 使用d分割符拼接v,如果attribute为字符串则首先获取value的attribute属性然后用d分割符去拼接属性值

{{ v|last }}

说明: 返回序列对象v的最后一个元素

{{ v|length }}

说明: 说明返回对象的长度

{{ v|list }}

说明: 转换v为列表,如果value为字符串则转换为字符列表

{{ v|lower }}

说明: 转换为小写

{{ v|map(filter/attribute=‘‘) }}

说明: filter过滤器作用域v的每个元素,attribute过滤出v中包含指定属性的元素


{%- for val_unit in  [1024, 2048, 4096]|map(‘filesizeformat‘) -%}
    <p>{{ val_unit }}</p>
{%- endfor -%}

{{ v|pprint(verbose=False) }}

说明: 更优美的方式打印,用于调试

{{ v|random }}

说明: 从序列对象中随机返回一个元素

{{ v|replace(old, new, count=None) }}

说明: 将v中的old替换为new,如果指定了count定义替换n次

{{ v|reverse }}

说明: 反转value序列

{{ v|round(precision=0, method=‘common‘) }}

说明: 四舍五入v的值precision表示保留小数点几位,method的值可以为common/ceil/floor

{{ v|safe }}

说明: 标记value为安全字符串,按照正常规则去解析

{{ value|sort(reverse=False, case_sensitive=False, attribute=None) }}

说明: 如果指定attribute则按照attribute排序,reverse表示是否逆向排序,case_sensitive表示是否区分大小写


{%- for cur_item in data|sort(false, true, attribute=‘index‘) -%}
    <p>{{ cur_item[‘index‘] }}</p>
{%- endfor -%}

{{ v|string }}

说明: 将v对象转换为字符串

{{ v|striptags }}

说明: 清除v中的html/xml等文档中的标签

{{ v|sum(attribute=None, start=0) }}

说明: 如果设置了attribute则会获取v中所有元素的attribute属性的值的总和,start为初始值

{{ v|title }}

说明: 让v中的每个字母的首字母大写

{{ v|trim }}

说明: 去除v两边的空白

{{ v|truncate(length=255, kilwords=False, end=‘...‘) }}

说明: v字符串超过length长度的部分用...代替,如果要精确到字符的话需设置killwards为true,否则设置为false

{{ v|tojson|safe }}

说明: 将v转换为json,但是为了不被转义,常常配合safe在Javascript脚本中使用


<script type="text/javascript">
    var users = {{ users|tojson|safe }}
    for(var i=0; i<users.length; i++){
        console.log(users[i].name)
    }
</script>

{{ v|upper }}

说明: 把v中的所有字符转换为大写

{{ v|urlencode }}

说明: 将v进行URL编码

{{ v|urlize(trim_url_limit=None, nofollow=False) }}

说明: 转换v为可点击的url连接地址,trim_url_limit设置url显示的长度,超出长度用...填充

{{ v|wordcount }}

说明: 获取s中的单词的数量

2. 除了内置过滤器还支持自定义过滤器,非常简单可通过app.add_template_filter(func, name)函数或@@app.template_filter(name)修饰器来自定义过滤器,两种方法内部调用的都是app.jinja_env.filters[name] = func实现的,但并不推荐直接设置jinja2环境,所以还是推荐前两种方法


@monitor.app_template_filter(‘sub‘)
def sub_filter(inputs, start, end, step):
    return inputs[start:end:step]

# 测试相关

1. 测试用于对照普通表达式测试一个变量,内置的列表可参考(http://docs.jinkan.org/docs/jinja2/templates.html#builtin-tests)

{%- object is callable -%}

说明:测试object是否可以被调用

{%- value is defined -%}

说明:测试value是否被定义

{%- value is divisibleby(num) -%}

说明:测试value是否可被num整除

{%- value is escaped -%}

说明:测试value是否被转义过

{%- value is even -%}

说明:测试value是否为奇数

{%- value is iterable -%}

说明:测试value是否可迭代

{%- value is lower -%}

说明:测试value是否小写

{%- value is mapping -%}

说明:测试value是否是字典

{%- value is none -%}

说明:测试value是否为none

{%- value is number -%}

说明:测试value是否为字符串

{%- value is odd -%}

说明:测试value是否为奇数

{%- value is sequence -%}

说明:测试value是否是序列对象

{%- value is string -%}

说明:测试value是否为字符串

{%- value is undefined -%}

说明:测试value是否未定义

{%- value is upper -%}

说明:测试value是否为大写

2. 除了内置测试器还支持自定义测试器,非常简单,通过app.add_template_test(func, name)函数或@app.template_test(name)修饰器来自定义过滤器,两种方法内部调用的都是app.jinja_env.tests[name] = func实现的,但并不推荐直接设置jinja2环境,所以还是推荐前两种方法


@monitor.app_template_test(‘endswith‘)
def ends_width(s, suffix):
    return s.endswith(suffix)

# 全局函数

1. 如果说过滤器是一个变量转换函数,测试器是一个变了测试函数,那么全局函数就可以是任何函数,可以在任一场景使用,没有输入和输出值限制,全局函数可参考,http://docs.jinkan.org/docs/jinja2/templates.html#builtin-globals

range([start], stop[, step])

说明: 返回一个包含整等差级数的列表

dict(**items)

说明: 方便的字典替代品{‘foo‘: ‘bar‘}等价于dict(foo=bar)

lipsum(n=5, html=True, min=20, max=100)

说明: 在模版中生成lipsum乱数假文,默认会生成5段html,每段20到100词之间,如果html被禁用,则会返回常规文本,在测试布局时生成简单内容时很有用

joiner(sep)

说明: 初始化一个分割符,第一次调用返回空字符,以后调用返回分割符,常用于分割循环中的内容


<ul>
    {%- set sep = joiner(‘|‘) -%}
    {%- for title in [‘资产管理‘, ‘主机分组‘, ‘主机信息‘] -%}
        {%- set s = ‘<i>‘ ~ sep() ~ ‘</i>‘ if sep() -%}
        {{ s|safe }}
        <li><a href="http://www.mamicode.com/#">{{ title }}</a></li>
    {% endfor %}
</ul>

说明: 在模版中生成乱数假文,默认会生成5段html,每段在20到100词之间,如果HTML被禁用,会返回常规文本,在测试布局时生成简单内容很有用

cycler(*items)

说明: 周期性的从若干个值的循环,周期实例.next()返回当前项并跳到下一个,.reset()重置周期计到第一个项,.current返回当前项

{%- set cur_datas = range(1, 101, 1) -%}
{%- set row_class = cycler(‘light‘, ‘dark‘) -%}
<table style="margin: 0 auto;border-collapse: collapse" width="600px" border="1">
    {%- for tr in cur_datas|slice(10) -%}
        <tr class="{{ row_class.next() }}">
            {%- for td in tr -%}
                <td>{{ td }}</td>
            {%- endfor -%}
        </tr>
    {%- endfor -%}
</table>

join(sep=‘,‘)

说明: 连接多个项


{{ range(10)|join(‘,‘) }}

2.  除了内置全局函数还支持自定义全局函数,非常简单,通过app.add_template_global(func, name)函数或@app.template_global(name)修饰器来自定义全局函数,两种方法内部调用的都是app.jinja_env.globals[name] = func实现的,但并不推荐直接设置jinja2环境,所以还是推荐前两种方法


@monitor.app_template_global(‘endswith‘)
def ends_width(s, suffix):
    return s.endswith(suffix)

# 注释相关

1. 模版中注释默认使用{# ... #},可以注释多行,在调试或添加模版信息时非常有用


{# 说明: 创建宽度为600px并居中对齐的一个10行10列的表格,并隔行换色
    <table style="margin: 0 auto;border-collapse: collapse" width="600px" border="1">
        {%- for tr in table_tds|slice(10) -%}
            {% if loop.index is odd %}
                <tr bgcolor="#ffffff">
                    {%- for td in tr -%}
                        <td>{{ td }}</td>
                    {%- endfor -%}
                </tr>
            {%- else -%}
                <tr bgcolor="#a9a9a9">
                    {%- for td in tr -%}
                        <td>{{ td }}</td>
                    {%- endfor -%}
                </tr>
            {%- endif -%}
        {%- endfor -%}
    </table>
 #}

# 空白控制

1. 默认模版引擎不会对空白处理,所以模版中的代码块中的每个空白都会原封不动返回,可以通过{%- ... -%}手动剥离块前或块儿后的空白,但需要注意的是-和{% %}不能有空白,否则无效


{%- for num in range(10) -%}
    {{ num }}
{%- endfor -%}

说明: 如上加-剥离空白后返回的是0123456789,而没有加-会发现返回的是0 1 2 3 4 5 6 7 8 9,每个项都多出一个空白,强烈推荐大家养成加-的习惯

# 转义相关

1.单行可用{{ ‘{{}}‘ }}作为原始字符串实现转义,多行可使用raw(如展示Jinja2语法的实例)


<li>{{ ‘{{‘ }}</li>
{% raw %}
    {% for item in seq %}
        <li>{{ item }}</li>
    {% endfor %}
{% endraw %}

# 模版继承

1. Jinja2最强大的部分就是模版继承,模版继承允许你构建一个包含你站点公共元素的基本模版‘骨架‘,并定义子模版可以覆盖的块


<!DOCTYPE html>
<html {% block  html_attribs %}{%- endblock -%}>
{%- block html -%}    
    <head>
    {%- block head -%}    
        {% block meta %}
        {%- endblock -%}
        {% block styles %}
        {%- endblock -%}
    {%- endblock -%}    
    </head>
    <body {%- block body_attribs -%}{%- endblock -%}>
    {%- block body -%}
        {%- block scripts -%}
        {%- endblock -%}
    {%- endblock -%}
    </body>
{%- endblock -%}    
</html>

{%- extends ‘base.html‘ -%}
{%- block head -%}
    {{ super() }}
    {#- 头部区 -#}
{%- endblock -%}
{%- block body -%}
    {{ super() }}
    {#- 主体区 -#}
{%- endblock -%}
{%- block styles -%}
    {{ super() }}
    {#- 样式区 -#}
{%- endblock -%}
{%- block scripts -%}
    {{ super() }}
    {#- 脚本区 -#}
{%- endblock -%}

说明: {% block ... %} ... {% endblock %}表示子模板需要填充的块,{% extends ‘....html‘ %}表示此模版继承自另一个模版,所以首先会定位父模版(应该作为第一个标签),默认继承时从templates目录查找,模版引擎也支持相对路径,但是需要注意的是同一个模版不能出现同名block,但是可以通过{{ self.block_name() }}在同一个页面调用指定的block块儿

# 嵌套相关

1. {%- block ... -%}{{ var }}{%- endblock -%}块内的变量var不允许访问块外作用域的变量,如果需要添可为block添加修饰scoped才可以获取块外的变量的值

# 控制结构

1. for可遍历可迭代对象中的每项,在模版循环体中支持特殊变量访问

loop.index

说明: 当前循环迭代的次数(从1开始)

loop.index0

说明: 当前循环迭代的次数(从0开始)

loop.revindex

说明: 到循环结束需要迭代的次数(从1开始)

loop.revindex0

说明: 到循环结束需要迭代的次数(从0开始)

loop.first

说明: 如果是第一次迭代,返回True

loop.last

说明: 如果是最后一次迭代,返回True

loop.length

说明: 序列中的项目数

loop.cycle

说明: 在一串序列周期间取值


{%- for cur_item in data -%}
    <P>总计需要循环的次数 : {{ loop.length }}</P>
    <p>从data中随机取数字: {{ loop.cycle(*data) }}</p>
    {%- if loop.first -%}
        <p>loop start ....</p>
    {%- endif -%}
    <hr />
    <p>当前循环计数(1开始): {{ loop.index }}</p>
    <p>当前循环计数(0开始): {{ loop.index0 }}</p>
    <p>剩余循环计数(1开始): {{ loop.revindex }}</p>
    <p>剩余循环计数(0开始): {{ loop.revindex0 }}</p>
    {%- if loop.last -%}
        <p>loop end....</p>
    {%- endif -%}
{%- endfor -%}

说明: for循环中loop.cycle()辅助函数,伴随循环字符串/变量列表中周期取值

2. 模版中循环不支持break/continue,但支持联合if跳过,如果过滤了所有项目而没有执行循环可使用else渲染替换块儿


{%- for cur_item in data if cur_item is odd  -%}
    <p>{{ cur_item }}</p>
{%- else -%}
    <p>nothing</p>
{%- endfor -%}

3. 模版支持递归循环,常用在站点地图,要使用递归循环,需要在循环定义中加上recursive修饰,并在你想递归的地方调用loop变量


<ul>
    {%- for cur_link in links recursive -%}
        <li><a href="http://www.mamicode.com/{{ cur_link[‘href‘] }}">{{ cur_link[‘caption‘] }}</a></li>
        {%- if cur_link[‘children‘] -%}
            <ul>
                {{ loop(cur_link[‘children‘]) }}
            </ul>
        {%- endif -%}
    {% else %}
        <li>no item found.</li>
    {% endfor %}
</ul>

注意: 如上代码{{ loop( ... ) }}外层必须还要包括一个ul,不然递归循环时生成的li依然放在第一层的ul中,页面的结构会错乱

4. if语句可以用于内联表达式并作为循环过滤,而且支持用elif和else来构建多个分支


{% extends layout_template if layout_template is defined else ‘master.html‘ %}

5. 宏类似常规编程语言中的函数,用于把常用的作为可重用函数,取代重复定义,如果需要宏在不同模版中引用需使用import导入,宏内部可以访问三个特殊变量

varargs

说明: 如果有多余宏位置参数,它们会作为列表的值保存在varargs变量上

kwargs

说明: 如果有多余宏键值参数,它们会作为列表的值保存在kwargs变量上

caller

说明: 如果宏通过call标签调用,调用者会作为可调用的宏存储在这个变量中

name

说明: 宏的名称

arguments

说明: 一个宏接受的参数名的元素

defaults

说明: 默认值的元素

catch_kwargs

说明: 如果宏接受额外的关键字参数,为True

catch_varargs

说明: 如果宏接受额外的位置参数,为True

caller

说明: 如果宏访问特殊的caller变量由call标签调用,为True


{%- macro list_user(users) -%}
    <table>
        <tr>
            <th>name</th>
            <th>option</th>
        </tr>
        {%- for user in users -%}
            <tr>
                <td>{{ user.name }}</td>
                <td>{{ caller(user.id) }}</td>
            </tr>
        {%- endfor -%}
    </table>
{%- endmacro -%}
{%- block body -%}
    {%- set users = [
                        {‘id‘: 1, ‘name‘: ‘李满满‘},
                        {‘id‘: 2, ‘name‘: ‘刘珍珍‘},
                    ] -%}
    {%- call(id) list_user(users) -%}
        <a href="http://www.mamicode.com/#user_edt#{{ id }}">编辑</a>
        |
        <a href="http://www.mamicode.com/#user_del#{{ id }}">删除</a>
    {%- endcall -%}
{%- endblock -%}

注意:如果一个宏的名称以下划线开始,它无法被导入到其它模版,如上简单演示了macro宏利用caller()访问调用者内容的例子,caller()中可以添加参数也可以不添加参数,非常灵活

6. 宏常存放使用的代码,这些宏可以被导入,并在可通过import/from import导入但默认不传递到当前上下文,只作为宏的存放处,如果要实现类似include支持传递到当前上下文,需要结合with context


{% import ‘forms.html‘ as forms with context %}
{% from ‘forms.html‘ import input as input_field, textarea with context %}


# 语句过滤

说明: 过滤器filter可以调用所有jinja2的过滤器,包含在filter节中的数据都会被渲染


{%- filter upper -%}
    <p>All html contents will be uppercase.</p>
{%- endfilter -%}

# INCLOUD

说明: incloud可以包含单个文件也可以包含一个文件列表,ignore missing可以忽略文件不存在导致的异常,with context时incloud包含的可渲染变量会自动被渲染,当然without context自然是禁用它

{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}
{% include [‘page_detailed.html‘, ‘page.html‘] %}
{% include [‘special_sidebar.html‘, ‘sidebar.html‘] ignore missing %}

# 上下环境

说明: FLASK每个请求都有生命周期,请求会携带请求上下文,作为在请求中渲染的模版,自然也是在请求的声明周期内,所以FLASK的模版自然可以使用到请求上下文的环境变量,及一些辅助函数

1. 模版中支持请求对象request所有的属性和方法,但是需要注意的是在非请求上下中这个对象是不可用的


{{ request.url }}

2. 模版中支持会话对象session所有的属性和方法,但是需要注意的是在非请求上下中这个对象是不可用的


{{ session.user }}

3. 模版中支持全局对象g所有的属性和方法,但是需要注意的是在非程序或请求上下中这个对象是不可用的


{{ g.user }}

4. 模版中支持配置对象config所有的属性和方法,但是需要注意的是在非程序或请求上下中这个对象是不可用的


{{ config.DEBUG }}

5. 模版中支持url_for()函数,可以快速获取以及构建URL,FLASK也将此函数引入模版中


<a href="http://www.mamicode.com/{{ url_for(‘monitor.index‘) }}">monitor</a>

6. 模版中还支持自定义上下文变量和函数,非常简单只需要用@app.context_processor去修饰一个返回字典的函数,字典中的键就可以在模版中直接使用,而字典中的可以是任何值,可以是变量可以是函数


@monitor.context_processor
def injection():
    area = u‘国内‘
    def hosts():
        return [h for h in xrange(10)]
    return {
        ‘area‘: area,
        ‘hosts‘: hosts
    }

# 模版扩展

说明: 除了以上的功能外,JinJa2还支持扩展或插件开发,允许第三方通过开发新的扩展或插件,当然它自身也提供了一些扩展功能,只是默认没有开启,咱们只是简单讲下怎么启用扩展,至于怎么使用就自己看官网文档了~


extensions = [‘jinja2.ext.autoescape‘,
         ‘jinja2.ext.with_‘,
         ‘jinja2.ext.do‘,
         ‘jinja2.ext.i18n‘,
         ‘jinja2.ext.loopcontrols‘]
jinja_env = Environment(extensions=extensions)
for extension in extensions:
    app.jinja_env.add_extension(extension)



本文出自 “满满李 - 运维开发之路” 博客,请务必保留此出处http://xmdevops.blog.51cto.com/11144840/1868238

网站后端_Python+Flask.0014.FLASK模版相关之使用JinJa2模版渲染?