【Flask】 Jinja2模板语言

Jinja2是Python Web编程中的主流模板语言。因为flask是基于Jinja2和Werkzeug发展而来,在安装flask的时候jinja2自动就被装在上面了。

  之前提到了很多Jinja2的使用方法啊,下面将补充或者更加详细地说明Jinja2作为模板语言的使用。

  【Jinja2官方文档】http://docs.jinkan.org/docs/jinja2/

■  基本语义

  看过之前模板的例子就知道,Jinja2做构成的模板文件中,文本内容大致可以分成几个种类。比如特殊文本(不进行转义,比如HTML,XML格式的文本)、{{  }}表示的变量或者宏调用、{%  %}表示逻辑控制,{#  #}表示注释,其中内容不被模板引擎转义。

■  set语句赋值

  在之前的所有例子中,模板中变量的值似乎只有一个来源,就是来自于后端的提供。但是实际上我们可以在模板这个层面对变量进行赋值,就是通过了{% set variable_name="value" %}的形式。这赋予了模板在内部进行变量赋值的能力,提高了灵活性。如果想要set一个变量的值为某个函数的返回值的话,那么那个函数不用{{ }}包着。比如{% set action_view = url_for('main.upload') %}而不是{{ url_for('main.upload') }}。

■  过滤器

  Jinja2可以使用过滤器,对传入模板的第一手数据进行二次加工。过滤器的形式是以管道符号连接第一手数据和二次处理数据的方法。比如在模板中写:

{{ my_variable|default("my_variable is not defined") }}

  的意思就是说在此处的第一手数据是由后端传过来的my_variable这个变量的值。如果这个变量没有被定义,那么就有default过滤器方法指定了它没有被定义时应该显示什么东西。过滤器方法有很多很多,各种各样的过滤器方法使得数据从后端传入前段之后还能再进行灵活的操作,从而提高了整个编程的便利性和敏捷性。其他常用或者我看着比较有用的过滤器方法还有:

  ****以下说明中的第一个参数都是指通过管道传过去的值,实际写在管道右边的方法参数应该比下面说明中的少一个,就像python面向对象编程中的self参数那样的感觉****

  attr(object,name)  比如account|attr("name")中account预期是一个字典(或者按照JS的说法是一个object),此时这个地方就是现实account['name']的值

  capitalize(s)  把字符串的首字母大写,后面的全部小写

  default(value,default_value=u'',boolean=False)  设置默认值,如果没有给出value,那么就用default_value的值(即空字符串)

  filesizeformat(value,binary=False)  接受一个数值然后转化成容易阅读的字节形式比如1.3MB,305Byte等,比如{{ size|filesizeformat() }}

  float(value,default=0)  接受一个值并将其转化为float类型

  escape(string)  把字符串中HTML特殊字符比如<,>,&等反转义成HTML的表达方式

  groupby(value,attribute)  按照指定的共有属性将(一个字典组成的列表)集合进行分组,返回一个字典组成的列表。这个列表中的每个字典都有两项,第一项的key是grouper然后value是根据groupby区分后得到的那个原字典中key的值。说了一串自己都晕了,下面给个例子。。

  比如模板是这个样子的:

复制代码
    <ul>
        {% for group in persons|groupby('gender') %}
            <li>{{ group.grouper }}<ul>
            {% for person in group.list %}
                <li>{{ person.first_name }} {{ person.last_name }}</li>
            {% endfor %}</ul></li>
        {% endfor %}
    </ul>
复制代码

  而作为persons传入的数据lst是这个样子的:

    lst = []
    lst.append({"gender":"male","first_name":"Frank","last_name":"Takanashi"})
    lst.append({"gender":"female","first_name":"Bob","last_name":"Kazuya"})
    lst.append({"gender":"female","first_name":"Ocean","last_name":"King"})

  那么最终得到渲染完成的文件是长这个样子的:

复制代码
<ul>
     <li>female
       <ul>
          <li>Bob Kazuya</li>
           <li>Ocean King</li>
        </ul>
      </li>
      <li>male
        <ul>
          <li>Frank Takanashi</li>
        </ul>
       </li>
</ul>
复制代码

  int(value,default)  把值转化为一个int类型

  join(value,sep=u'')  接受一个序列类型的对象,然后通过sep指定的字符将所有序列元素连接在一起成为一个字符串。sep默认是空字符串

  last/first/random(seq)  返回序列的最后/最前/随机一个元素

  replace(string,old,new,count=None)  接受一个字符串,将其中的old部分全部转化为new,从左到右替换count次,如果不指定count是只替换第一个扫描到的

  reverse(value)  接受一个可迭代对象,然后返回其反序序列的迭代器

  sort(value,reverse=False,case_sensative=False,attribute=None)  对可迭代的对象进行排序,默认情况下以升序大小写不敏感的方式排序。

  striptags(value)  把一段比如XML格式的文本中的标签都去掉

  sum(iterable,attribute=None,start=0)  对可迭代对象进行求和,如果需要对其某个特定属性的值求和的话可以设置attribute属性

  title(string)  将字符串转换为标题格式显示

  trim(value)  去除开头和末尾的所有空格,相当于strip()

  truncate(s,length=255,killwords=False,end='...')  这是个比较有有意思的方法,它把字符串转化成简略形式,比如"foo bar"|truncate(5)可以得到foo ...,如果"foo bar"|truncate(5,True)可以得到foo b...

  round(value,precision=0,method='common')  对数字进行四舍五入,precision表示小数点后保留几位。method除了common是四舍五入规则外,floor是向下抹平,ceil是向上抹平。

  * 今天遇到了round的一个坑,就是需要注意round前面的东西一定要是一个整体。当然1.23|round之类,直接写数字没问题,当使用算式时请注意一定要把算式用括号括起来才能使过滤器有效。比如 num/1024.0 | round是不会有四舍五入之类的效果的,只有(num/1024.0) | round才行。

  wordcount(s)  计算字符串中单词的个数

  

  以上还不是过滤器方法的全部,一般而言这些处理都可以放在python中做,python也可以做的非常好,但是这样的话势必要往模板中传递很多很多变量。如果可以在模板这个层面对这些数据进行一些二次处理的话就可以让后端和前端之间的交互体量变小,提高编程的便利性。

■  逻辑结构

  Jinja2中和一般编程语言中一样,可以有if/else,for,is等等关键字作为逻辑控制的节点。而这些关键字必须是放在{% %}或者{{ }}里面的。

  ●  is关键字判断条件

  利用is关键字和所谓的“测试方法”可以获得一个布尔值,从而利用其进行一些逻辑判断。比如{{ name is defined }}可以返回True/False来体现name这个变量是否被定义了。类似的“测试方法”还有:

  callable(object)  检查一个对象是否可调用

  defined/undefined(object)  检查一个对象是/否被定义了

  divisibleby(value,num)  检查value能否被数字num整除

  escaped(value)  检查一个对象是否被转码了

  iterable(value)  检查一个对象是否可迭代

  lower/upper(value)  检查一个对象是否都小/大写

  none(value)  检查一个对象是否是None

  number(value)  检查一个对象是否是数字

  sameas(value,other)  检查一个对象是否和other这个对象是同一个对象

  用法:{% if loop.index is divisibleby(3) %}就表示如果loop.index这个值能够被3整除的话就进入这个逻辑分支

  除了上面这些带参数的判断条件,也有一些不带参数的条件,在Jinja2里被称为测试器(test)。比如:

  defined/none  检查一个对象是否被定义,其实效果和{% if var %}是一样的

  upper/lower  检查一个字符串对象是否都是大写或小写

  string  {% if var is string %}检查对象是不是一个字符串

  odd/even  检查一个整数对象是否是奇/偶数

  iterable  检查一个对象是不是可迭代对象,简单来说就是看是不是列表之类的序列类型

  mapping  检查一个对象是不是k-v对形式的对象,比如字典

  ●  自定义测试器

  如果内置的一些测试器不能满足我们的需求,那么我们可以自定义一些测试器。具体的做法是在python代码中写一个测试函数,然后用app.add_template_test方法把这个测试器和app对象关联起来。比如下面这个:

复制代码
def over_five(string):
    #接受的一定是一个字符串对象,然后可以在函数中对这个字符串做出处理
    return int(string) > 5

app.add_template_test(over_five,'my_test')

'''
然后在模板中就可以这么用了:
{% if num is my_test %}
<p>{{ num }} is bigger than 5</p>
{% else %}
<p>{{ num }} is smaller than 5</p>
{% endif %}
'''
复制代码

  

  ●  判断语句

  判断语句没什么好说的了,反正就是{% if ... %}{% elif ... %}{% else %}{% endif %}的格式。

  ●  循环语句

  Jinja2中的循环只能通过for语句,而且不具备break,continue之类的循环控制功能。但是在for循环的作用域内,可以访问特殊对象loop的一些属性来获得循环的一些信息。loop具有以下属性:

  loop.index  当前循环迭代的次数(从1开始计数)

  loop.index0  从0开始计数的循环迭代次数

  loop.revindex  到循环结束为止需要迭代的次数  

  loop.first  是否是第一次迭代  

  loop.last  是否是最后一次迭代

  loop.length  序列中项目的数量  

  loop.cycle  在一串序列间取值的辅助函数

  比如下面这个代码的用例:

复制代码
{% for user in users %}
  <li>
   {% if not loop.first %} {{ user.username }} {% endif %} </li> {% endfor %}
复制代码

  就是说假如是循环中的第一个,就不显示它的username了。

■  模板继承

  之前在简介中也说过好多次,jinja2的模板是可以进行继承的。采用关键字{% extends "parent.html" %}的形式。

  父模板中可以有多个不重名的{% block xxx %}{% endblock %},在子模板中通过{% block xxx %}可以指定修改某个block中的内容。更加详尽的内容都写在flask的大概介绍里,就不重复了。

 ■  独立使用jinja2

  jinja2作为一个插件很广泛地用于flask,django等框架中。但是也不能忘记,jinja2本身就是一个python的模块。在非框架的环境中,我们也可以通过其API单独进行jinja2的调用。常用的方法是这样子的:

1
2
3
4
5
from jinja2 import Template
 
= Template('Hello, {{ name }}')
print t.render(name='Frank')
# 输出 Hello, Frank

   可以看到,通过给Template类传递一段模板代码,然后调用其render方法,并且给出模板中变量的定义,就可以得到渲染出来的代码了。但是这样有一点不好就是要把模板内容全部写出来,最好是有传递文件名来指定一个模板的。这就要用到jinja2中的Envrionment和Loader两个概念:

复制代码
from django import Environment,FileSystemLoader

env = Environment(loader=FileSystemLoader('/path/to/directory'))
t = env.get_template('test.html')

print t.render(name='Frank')
# 将模板文件test.html按照给出的参数进行渲染
复制代码

   /path/to/directory是test.html所在的目录,该目录下所有其他文件也可以通过get_template方法来作为一个模板文件获取出来。这里直接写模板文件名说明模板文件就在目录下,如果有多层级目录也可以写/path/to/file.html。FileSystemLoader应该是一个常用的Loader,它将某个指定的目录作为模板的存放目录加入环境中。除了FileSystemLoader以外,还有其他的一些Loader比如PackageLoader,PackageLoader('application','templates')将包application(中有__init__.py文件)中的templates目录作为模板的存放目录。

  模板内容渲染成实际代码的逻辑和过程和框架中的jinja2是一样的。

原文地址:https://www.cnblogs.com/ExMan/p/9842750.html