Flask之模板

06-22 1337阅读

 前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 

Flask之模板

目录

一、模板的基本用法

1.1、创建模板

1.2、模板语法

1.3、渲染模板

二、模板辅助工具

2.1、上下文

2.2、全局对象

2.3、过滤器

2.4、测试器

2.5、模板环境对象

三、模板组织结构

3.1、局部模板

3.2、宏

3.3、模板继承

四、模板进阶实践

4.1、空白控制

4.2、加载静态文件

4.3、消息闪现

4.4、自定义错误页面

4.5、JavaScript和CSS中的Jinja2

致谢


一、模板的基本用法

1.1、创建模板

假设我们要编写一个用户的电影清单页面(用户信息、用户收藏的电影列表等)。首先创建一些虚拟数据用于测试显示效果:

 user = {
     'username': 'Grey Li',
     'bio': 'A boy who loves movies and music',
 }
 ​
 movies = [
     {'name': 'My Neighbor Totoro', 'year': '1988'},
     {'name': 'Three Colours trilogy', 'year': '1993'},
     {'name': 'John Harrison', 'year': '1994'},
     {'name': 'Perfect Blue', 'year': '1997'},
 ]

在templates目录下创建一个watchlist.html作为模板文件,然后使用Jinja2支持的语法在模板中操作这些变量:

 
 
 
     
     https://blog.csdn.net/lerp020321/article/details/{{ user.username }}'s Watchlist
 
 
 { url_for('index') }}">$larr; Return
 

https://blog.csdn.net/lerp020321/article/details/{{ user.username }}

 {% if user.bio %}      https://blog.csdn.net/lerp020321/article/details/{{ user.bio }}  {% else %}      This user has not provided a bio  {% endif %}  {# 下面是电影清单(这是注释) #}  
https://blog.csdn.net/lerp020321/article/details/{{ user.username }}'s Watchlist (https://blog.csdn.net/lerp020321/article/details/{{ movies|length }}):
 
        {% for moive in movies %}          
  • https://blog.csdn.net/lerp020321/article/details/{{ movie.name }} - https://blog.csdn.net/lerp020321/article/details/{{ moive.year }}
  •     {% endfor %}  
   

Jinja2里常见的三种定界符:

(1)语句

比如if判断、for循环等:

{% ... %}

(2)表达式

比如字符串、变量、函数调用等:

https://blog.csdn.net/lerp020321/article/details/{{ ... }}

(3)注释

{# ... #}

另外,在模板中Jinja2 支持用"."获取变量的属性,比如user字典中的username键值通过"."获取,即user.username,在效果上等同于user['username']。

1.2、模板语法

Jinja2运行在模板中使用大部分Python对象,比如字符串、列表、字典、元组、整型、浮点型、布尔值等。支持基本的运算符号(+、-、*、/等)、比较符号(==、 !=等)、逻辑符号(and、or、not等)以及in、is、None和布尔值(True、False)。

还提供多种控制结构来控制模板的输出,其中for和if是最常用的两种。语句使用{% ... %}标识,尤其注意,在语句结束的地方,我们必须添加结束标签:

 {% if user.bio %}
     https://blog.csdn.net/lerp020321/article/details/{{ user.bio }}
 {% else %}
     This user has not provided a bio.
 {% endif %}

和Python一样,for语句用来迭代一个序列:

 
        {% for moive in movies %}          
  • https://blog.csdn.net/lerp020321/article/details/{{ movie.name }} - https://blog.csdn.net/lerp020321/article/details/{{ moive.year }}
  •     {% endfor %}  

在for循环内,Jinja2提供了多个特殊变量,常用的循环变量:

变量名说明
loop.index当前迭代数(从1开始计数)
loop.index0当前迭代数(从0开始计数)
loop.revindex当前反向迭代数(从1开始计数)
loop.revindex0当前反向迭代数(从0开始计数)
loop.first如果是第一个元素,则为True
loop.last如果是最后一个元素,则为True
loop.previtem上一个迭代的条目
loop.nextitem下一个迭代的条目
loop.length序列包含的元素数量

1.3、渲染模板

渲染一个模板就是执行模板中的代码,并传入所有在模板中使用的变量,渲染后的结果就是我们要返回给客户端的HTML响应。使用Flask提供的渲染函数render_template():

 from flask import Flask,render_template
 ...
 @app.route('/watchlist')
 def watchlist():
     return render_template('watchlist.html', user=user, movies=movies)

除了render_template()函数,Flask还提供了一个render_template_string()函数用来渲染模板字符串。

其他类型的变量通过相同的方式传入。示例:

 

这是列表my_list的第一个元素:https://blog.csdn.net/lerp020321/article/details/{{ my_list[0] }}

 

这是元组my_tuple的第一个元素:https://blog.csdn.net/lerp020321/article/details/{{ my_tuple[0] }}

 

这是字典my_dict的键为name的值:https://blog.csdn.net/lerp020321/article/details/{{ my_dict['name'] }}

 

这是函数my_func的返回值:https://blog.csdn.net/lerp020321/article/details/{{ my_func() }}

 

这是对象my_object调用某方法的返回值:https://blog.csdn.net/lerp020321/article/details/{{ my_object.name }}

二、模板辅助工具

为了方便测试,在templates目录下创建一个根页面模板index.html:

 from flask import render_template
 ​
 @app.route('/')
 def index():
     return render_template('index.html')

2.1、上下文

除了渲染时传入变量,也可以在模板中定义变量,使用set标签:

 {% set navigation = [('/','Home'),('/about','About')] %}

也可以将一部分模板数据定义为变量,使用set和endset标签声明开始和结束:

 {% set navigation %}
     
  • Home      
  • About  {% endset %}

    2.1.1、内置上下文变量

    Flask在模板上下文中提供了一些内置变量,可以在模板中直接使用:

    变量说明
    config当前的配置对象
    request当前的请求对象,在已激活的请求环境下可用
    session当前的会话对象,在已激活的请求环境下可用
    g与请求绑定的全局变量,在已激活的请求环境下可用

    2.1.2、自定义上下文

    如果多个模板都需要使用同一变量,设置一个模板全局变量以供使用。Flask提供了一个app.context_processor装饰器,可以用来注册模板上下文处理函数,完成统一传入变量操作:

     @app.context_processor
     def inject_foo():
         foo = 'I am foo.'
         return dict(foo=foo)

    当我们调用render_template()函数渲染任意一个模板时,所有使用app.context_processor装饰器注册的模板上下文处理函数都会被执行,这些函数的返回值会被添加到模板中,因此我们可以在模板中直接使用foo变量。

    除了使用app.context_processor装饰器,也可以直接将其作为方法调用,传入模板上下文处理函数:

     ...
     def inject_foo():
         foo = 'I am foo.'
         return dict(foo=foo)
     app.context_processor(inject_foo)

    使用lambda简化为:

     app.context_processor(lambda:dict(foo='I am foo.'))

    2.2、全局对象

    全局对象值在所有模板中都可以直接使用的对象,包括在模板中导入的模板。

    2.2.1、内置全局函数

    Jinja2在模板中默认提供了一些全局函数(部分):

    函数说明
    range([satrt,]stop[,step])和Python中的range()用法相同
    lipsum(n=5,html=True,min=20,max=100)生成随机文本,可以在测试时用来填充页面。默认生成5段HTML文本,每段包含20-100个单词
    dict(**items)和Python中的dict()用法相同

    Flask也在模板中内置了两个全局函数:

    函数说明
    url_for()用于生成URL的函数
    get_flashed_messages()用于获取flash消息的函数

    url_for()用来获取URL,用法和在Python脚本中相同。在实际的代码中,这个URL使用url_for()生成,传入index视图的端点:

     { url_for('index')}}">← Return

    2.2.2、自定义全局函数

    使用app.template_global装饰器直接将函数注册为模板全局函数。(仅用于注册全局函数)

     # 把bar()函数注册为模板全局函数
     @app.template_global
     def bar():
         return 'I am bar'

    默认使用函数的原名称传入模板,在app.template_global()装饰器中使用name参数可以指定一个自定义名称。

    2.3、过滤器

    过滤器修改和过滤变量值的特殊函数,过滤器和变量用一个竖线(管道符号)隔开,需要参数的过滤器可以像函数一样使用括号传递。

     https://blog.csdn.net/lerp020321/article/details/{{ name|title }}
     # 这会将name变量的值标题化,相当于Python中调用name.title()。
     ​
     https://blog.csdn.net/lerp020321/article/details/{{ movies|length }}
     # 获取moives长度,相当于Python值调用len(moives)

    另一种方法是将过滤器作用于部分模板数据,使用filter标签和endfilter标签声明开始和结束:

     {% filter upper %}
      This text bacomes uppercase.
     {% endfilter %}

    2.3.1、内置过滤器(部分)

    过滤器说明
    escape(s)转义HTML,别名为e
    first(seq)返回序列的第一个元素
    last(seq)返回序列的最后一个元素
    length(object)返回变量的长度
    random(seq)返回序列中的随机元素

    在使用过滤器时,列表中过滤器函数的第一个参数表示过滤的变量值(value)或字符串(s),即竖线符号左侧的值,其他的参数可以通过括号传入。

    过滤器可以叠加使用:

     # 为变量name设置默认值,并将其标题化
     

    Hello,https://blog.csdn.net/lerp020321/article/details/{{ name|default('陌生人')|title }}!

    根据Flask设置,Jinja2会自动对模板中的变量进行转义,因此我们不需要手动使用escape过滤器或调用excape()函数对变量进行转义。若想避免转义,将变量作为HTML解析,可以对变量使用safe过滤器或者在渲染前将变量转换为Markup对象:(不要对用户输入的内容使用safe,容易被攻击)

     # safe过滤器
     https://blog.csdn.net/lerp020321/article/details/{{ sanitied_text|safe }}
     # 转换为markup对象
     from flask import Markup
     ​
     @app.route('/hello')
     def hello():
         text = Markup('

    Hello,Flask!

    ')      return render_template('index.html',text=text) # 这时可以在模板中直接使用https://blog.csdn.net/lerp020321/article/details/{{ text }}

    2.3.2、自定义过滤器

    使用app.template_filter()装饰器可以注册自定义过滤器:例注册一个musical过滤器:

     from flask import Markup
     ​
     @app.template_filter()
     def musical(s):
         return s + Markup(' ♫')

    与注册全局函数一样,可以在app.template_filter()中使用name关键字设置过滤器的名称,默认使用函数名称。用法与其他过滤器相同。

    2.4、测试器

    在Jinja2中,测试器(Test)是一些用来测试变量或表达式,返回布尔值的特殊函数。

     # number测试器用来判断一个变量或表达式是否为数字,我们使用is连接变量和测试器:
     {% if age is number %}
         https://blog.csdn.net/lerp020321/article/details/{{ age * 365 }}
     {% else %}
         无效的数字
     {% endif %}

    2.4.1、内置测试器(部分)

    测试器说明
    callable(object)判断对象是否可被调用
    defined(value)判断变量是否已定义
    undefined(value)判断变量是否未定义
    none(value)判断变量是否None
    number(value)判断变量是否是数字
    string(value)判断变量是否字符串
    sequence(value)判断变量是否是序列,比如字符串、列表、元组
    iterable(value)判断变量是否可迭代
    mapping(value)判断变量是否是匹配对象,比如字典
    sameas(value,other)判断变量与 other是否指向相同的内存地址

    使用测试器时,is的左侧是测试器函数的第一个参数(value),其他参数可以添加括号传入,也可以在右侧使用空格连接,以sameas为例:

     {% if foo is sameas(bar) %}...
     # 等同于
     {% if foo is sameas bar %}...

    2.4.2、自定义测试器

    使用app.template_test()装饰器来注册一个自定义测试器。例子:

     @app.template_test
     def baz(n):
         if n == 'baz':
             return True
         return False

    同样,可以使用关键字name指定名称,默认为函数名称

    2.5、模板环境对象

    在Jinja2中,渲染行为由jinja2.Environment类控制,所有的配置选项、上下文变量、全局函数、过滤器和测试器都存储在Environment实例上。当与Flask结合后,我们直接使用Flask创建的Environment对象,它存储在app.jinja_env属性上(其可以更改Jinja2设置)

     #使用variable_start_string和variable_end_string自定义变量定界符的开始和结束
     app = Flask(__name__)
     app.jinja_env.variable_start_string = '[['
     app.jinja_env.variable_end_string = ']]'

    模板环境中的全局函数、过滤器和测试器分别存储在Environment对象的globals、filters和tests属性中,都是字典对象。

    2.5.1、添加自定义全局对象

    和app.template_global()装饰器不同,直接操作globals字典允许我们传入任意Python对象,而不仅仅是函数,类似上下文处理函数的作用。

     # 添加全局函数bar和全局变量foo:
      def bar():
         return 'I am bar'
     foo = 'I am foo'
     app.jinja_env.globals['bar'] = bar
     app.jinja_env.globals['foo'] = foo

    2.5.2、添加自定义过滤器

     # 添加自定义过滤器smiling
     def smiling(s):
         return s + ':)'
     app.jinja_env.filters['smiling'] = smiling

    2.5.3、添加自定义测试器

     # 添加自定义测试器baz
     def baz(n):
         if n == 'baz':
             return True
         return False
     app.jinja_env.tests['baz'] = baz

    三、模板组织结构

    Jinja2还提供了一些工具来在宏观上组织模板内容。更好实践DRY(Don't Repeat Yourself)原则

    3.1、局部模板

    当多个独立模板都会使用同一块HTML代码时,我们把这部分代码抽离出来,存储到局部模板中。可以避免重复又方便统一管理。

    我们使用include标签插入一个局部模板,这会把局部模板的全部内容插在使用include标签的位置:

     {% include '_banner.html' %}

    为了和普通模板区分开,局部模板命名通常以一个下划线开始

    3.2、宏

    宏(macro)是Jinja2中的特性,类似Python中的函数。使用宏可以将一部分模板代码封装到宏里,使用传递的参数来构建内容,最后返回构建后的内容。

    为了便于管理,通常把宏存储到单独文件中(macros.html)。

    使用macro和endmacro标签声明宏的开始和结束,在开始标签中定义宏的名称和接收的参数:

     {% macro qux(amount=1) %}
         {% if amount ==1 %}
             I am qux.
         {% elif amount > 1 %}
             We are quxs.
         {% endif %}
     {% endmacro %}

    使用时使用import语句导入,然后作为函数调用,传入必要的参数:

     {% from 'macros.html' impor qux %}
     ...
     https://blog.csdn.net/lerp020321/article/details/{{ qux(amount=5) }}

    默认情况下包含(include)一个局部模板会传递当前上下文到局部模板,但导入(import)不会。换句话:当我们使用render_template()函数渲染一个foo.html模板时,这个foo.html的模板上下文中包含下列对象:

    • Flask使用内置的模板上下文处理函数提供的g、session、config、request。
    • 扩展使用内置的模板上下文处理函数提供的变量
    • 自定义模板上下文处理器传入的变量
    • 使用render_template()函数传入的变量
    • Jinja2和Flask内置及自定义全局对象
    • Jinja2内置及自定义过滤器
    • Jinja2内置及自定义测试器

      使用include标签插入的局部模板同样可以使用上述。而导入一个并非被直接渲染的模板,这个模板仅包含下列对象:

      • Jinja2和Flask内置及自定义全局对象
      • Jinja2内置及自定义过滤器
      • Jinja2内置及自定义测试器

        因此,若想在导入的宏中使用第一个列表中的2,3,4项,就需要在导入时显式地使用with context声明传入当前模板的上下文:

         {% from 'macros.html' impor foo with context %}

        3.3、模板继承

        Jinja2的模板允许定义一个基模板,把像导航栏、页脚等通用部分放在基模板中。

        3.3.1、编写基模板

        基模板存储程序页面固定部分,通常被命名为base.html或layout.html。

         
         
         
             
             {% block title %}Template - HelloFlask{% endblock %}
             {% block styles %}{% endblock %}
         
         
         
             
        • { url_for('index') }}">Home
                {% block content %}{% endblock %}         {% block footer %}         ...     {% endblock %}    {% block scripts %}{% endblock %}    

        当子模板继承基模板后,会自动包含基模板的内容和结构。在基模板定义块(block),在子模板中可以通过定义同名的块来执行继承操作。

        块的开始和结束用block和endblock标签声明,可以嵌套。为了避免块的混乱,块的结束标签可以指明块名,确保前后名称一致。

         {% block body %}
         ...
         {% endblock body %}

        3.3.2、编写子模板

        在子模板中只需要对特定的块进行修改。

         {% extends 'base.html' %}
         {% from 'macro.html' import qux %}
         ​
         {% block content %}
         {% set name='baz' %}
         

        Template

         
               
        • { url_for('watchlist') }}">Watchlist
        •      
        • Filter:https://blog.csdn.net/lerp020321/article/details/{{ foo|musical }}
        •      
        • Global:https://blog.csdn.net/lerp020321/article/details/{{ bar() }}
        •      
        • Test:{% if name is baz %}I am baz.{% endif %}
        •      
        • Macro:https://blog.csdn.net/lerp020321/article/details/{{ qux(amount=5) }}
        •  
         {% endblock %}

        extends标签声明扩展基模板,且必须是子模板的第一个标签。

        我们在基模板中定义了四个块,在子模板中,我们可以对父模板中的块执行两种操作:

        (1)覆盖内容

        在子模板创建同名的块,会使用子块的内容覆盖父块的内容。

        (2)追加内容

        使用Jinja2提供的super()函数进行声明,这会向父块添加内容,例如(向基模板中的styles块追加一行样式定义:

         {% block styles %}
             https://blog.csdn.net/lerp020321/article/details/{{ super() }}
             
             .foo{
                 color:red;
             }
             
         {% endblock %}

        四、模板进阶实践

        模板在Flask程序中的常见应用(加载静态文件和自定义错误页面)

        4.1、空白控制

        在实际输出的HTML文件中模板中的Jinja2语句、表达式和注释会保留移除后的空行。

        {% if user.bio %}
             https://blog.csdn.net/lerp020321/article/details/{{ user.bio }}
         {% else %}
             This user has not provided a bio.
         {% endif %}
         ​
         # 实际输出的HTML代码:
         ​
         https://blog.csdn.net/lerp020321/article/details/{{ user.bio }}
         ​
         This user has not provided a bio.

        若想在渲染时自动去掉这些空行,在定界符内侧添加减号。{%- endfor %}会移除该语句前的空白,同理,右边可以移除该语句后面的空白

        {% if user.bio -%}
             https://blog.csdn.net/lerp020321/article/details/{{ user.bio }}
         {% else -%}
             This user has not provided a bio.
         {%- endif %}
         ​
         # 现在输出的Html:
         https://blog.csdn.net/lerp020321/article/details/{{ user.bio }}
         This user has not provided a bio.

        除了使用减号,还可以使用模板环境对象提供的trim_blocks(删除语句后的第一个空行)和lstrip_blocks属性(删除语句所在行之前的空格和制表符)设置。

         app.jinja_env.trim_blocks = True
         app.jinja_env.lstrip_blocks = True
         ​
         # trim_blocks值的block指的是使用{%...%}界定的代码块,与继承的块无关

        宏内的空白制作不受terim_blocks和lstrip_blocks属性控制,我们需要手动设置:

         {% macro qux(amount=1) %}
             {% if amount ==1 -%}
                 I am qux.
             {% elif amount > 1 -%}
                 We are quxs.
             {%- endif %}
         {% endmacro %}

        事实上,我们没必要严格控制HTML输出,因为多余的空白并不影响浏览器的解析。

        4.2、加载静态文件

        在Flask程序中,默认将静态文件存储在与主脚本同级目录的static文件夹中。

        使用url_for()函数获取静态文件的URL在HTML模板中引用。Flask内置了用于获取静态文件的视图函数,端点值为static,它的默认URL规则为/static/path:filename,URL变量filename是相对于static文件夹根目录的文件路径。

        提示:若想使用其他文件夹来存储静态文件,可以在实例化Flask类时使用static_folder参数指定。在实例化Flask类时使用static_url_path参数则可以自定义静态文件的URL路径。

         
  • VPS购买请点击我

    文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

    目录[+]