Flask-jinja2学习(上)

jinja2的相关知识点


jinja2是python的一个web库,旨在灵活,快速和安全,可以简化python的html语法。

基本语法

  • 标记块开头的字符串 {%
  • 标记块结束的字符串 %}
  • 标记print语句开始的字符串 {{
  • 标记print语句结束的字符串 }}
  • 标记注释开头的字符串 {#
  • 标记注释结尾的字符串 #}

表达式种类

  • 最常用的变量,由Flask渲染模板时传过来,类似于 name
  • 任意一种python基础类型,例如{{ “hello” }},还有列表,元组,字典等等。
  • 运算。包括算数运算,比较运算,逻辑运算 {{False and True}}
  • 过滤器 “|” 和测试器 “is”
  • 函数调用,如{{ current-time() }},数组下标操作,{{ arr[1] }}
  • ‘in’ 操作符,如{{ 1 in [1,2,3] }}
  • 字符串连接符‘~’,和python里的‘+’一样。如{{ ‘hello’name‘!’ }}
  • ‘ if ’关键字,如{{ hi,%s %name if name }},这里的if不是控制语句

for 循环和 if else

<dl>
    {% for user in users if not user.hidden %}
        {% if loop.first %}
            <div>User List:</div>
        {% endif %}
        <div class="{{ loop.cycle('odd','even')}}">
            <dt>User No.{{loop.index}}</dt>
            <dd>{{ user.name }}</dd>
        </div>
        {% if loop.last %}
            <div>Total Users: {{ loop.length }}</div>
        {% endif %}
    {% else %}
        <div>No users found</div>
    {% endfor %}
</dl>

循环内置变量loop

  • loop.index 循环迭代计数(从1开始)
  • loop.index0 循环迭代计数(从0开始)
  • loop.revindex 循环迭代倒序计数(从len开始,到1结束)
  • loop.revindex0 循环迭代倒序计数(从len开始,到0结束)
  • loop.first 是否为循环第一个元素
  • loop.length 循环序列中的元素长度
  • loop.cycle 在给定的序列中循环
  • loop.depth 当前循环在递归的层级

忽略模板写法

<dl>
    <dl>忽略模板语法</dl>
    {% raw %}
        <ul>
            {% for item in items %}
            <li>{{ item }}</li>
            {% endfor %}
        </ul>
    {% endraw %}
</dl>

自动转义和给变量赋值

{% autoescape false %}
    <h1>Hello {{ name }}!</h1>
{% endautoescape %}
{% set items=[[1,2],[3,4,5]] %}
{{ items }}

with语句

<div>
    {% with foo =1 %}
        {% set bar =2 %}
        {{ foo + bar }}
    {% endwith %}
</div>
<div>
    {% with arr = ['Sunny','Rainy'] %}
        {{ arr }}
        {% do arr.append('ltf') %}
        {{ arr }}
    {% endwith %}
</div>

do 语法需要导入外部扩展

app.jinja_env.add_extension(‘jinja2.ext.do’)


上下文环境

Flask每个请求都有生命周期,在生命周期内请求有其上下文环境Request Context。作为在请求中渲染的模板,自然也在请求的生命周期内,所以Flask应用中的模板可以使用到请求上下文中的环境变量,及一些辅助函数。本文就会介绍下这些变量和函数。

请求对象request

request对象可以用来获取请求的方法”request.method”,表单”request.form”,请求的参数”request.args”,请求地址”request.url”等。它本身是一个字典。在模板中,你一样可以获取这些内容,只要用表达式符号”{{ }}”括起来即可。

会话对象session

session对象可以用来获取当前会话中保存的状态,它本身是一个字典。在模板中,你可以用表达式符号”{{ }}”来获取这个对象

全局对象g

全局变量g,用来保存请求中会用到全局内容,比如数据库连接。模板中也可以访问。

Flask配置对象config

导入的配置信息,就保存在”app.config”对象中。这个配置对象在模板中也可以访问。

url_for()函数

url_for()函数可以用来快速获取及构建URL,Flask也将此函数引入到了模板中,比如下面的代码,就可以获取静态目录下的”style.css”文件。

主程序

#加载扩展
app.jinja_env.add_extension('jinja2.ext.do')
app.secret_key = '123456'

@app.route('/')
def index():
    session['user'] = 'guest'
    g.db = 'mysql'
    return render_template('main.html')

main.html文件

<dl>
    <div>
    <p>{{ "请求的url为:"~request.url }}</p>
    <p>{{ "session保存的用户为:"+session.user }}</p>
    <p>DB数据库: {{ g.db}}</p>
    <p>Host配置:{{ config }}</p>
    </div>
</dl>
<dl>
    <div class="odd">

    </div>
</dl>

style.css文件

.odd {
    background-color: #BDF;
    width: 200px;
    height: 200px;
  }

自定义变量和函数

除了Flask提供的标准上下文变量和函数,我们还可以自己定义。

@app.context_processor
def appinfo():
    return dict(appname=current_app.name)
@app.context_processor
def get_current_time():
    def get_time(timeFormat="%b %d, %Y - %H:%M:%S"):
        return time.strftime(timeFormat)
    return dict(current_time=get_time)

函数用”@app.context_processor”装饰器修饰,它是一个上下文处理器,它的作用是在模板被渲染前运行其所修饰的函数,并将函数返回的字典导入到模板上下文环境中,与模板上下文合并。然后,在模板中”appname”就如同上节介绍的”request”, “session”一样,成为了可访问的上下文对象。

同理我们可以自定义上下文函数,只需将上例中返回字典的属性指向一个函数即可

<div>
    <p>当前 App is: {{ appname }}</p>
    <p>当前 Time is: {{ current_time() }}</p>
    <p>当前 Day is: {{ current_time("%Y-%m-%d") }}</p>
</div>

内置过滤器

字符串操作
    {{ '字符串操作....' }}
    {# 单词首字母大写 #}
    <p>{{ 'hello' | capitalize }}</p>

    {# 单词全小写 #}
    <p>{{ 'XML' | lower }}</p>

    {# 去除字符串前后的空白字符 #}
    <p>{{ '  hello  ' | trim }}</p>

    {# 字符串反转,返回"olleh" #}
    <p>{{ 'hello' | reverse }}</p>

    {# 格式化输出,返回"Number is 2" #}
    <p>{{ '%s is %d' | format("Number", 2) }}</p>

    {# 关闭HTML自动转义 #}
    <p>{{ '<em>name</em>' | safe }}</p>

    {% autoescape false %}
        {# HTML转义,即使autoescape关了也转义,可以缩写为e #}
        <p>{{ '<em>name</em>' | escape }}</p>
    {% endautoescape %}
数值操作
    {{ '数值操作...' }}
    {# 四舍五入取整,返回13.0 #}
    <p>{{ 12.8888 | round }}</p>

    {# 向下截取到小数点后2位,返回12.88 #}
    <p>{{ 12.8888 | round(2, 'floor') }}</p>

    {# 绝对值,返回12 #}
    <p>{{ -12 | abs }}</p>
列表操作
    {{ '列表操作...' }}
    {# 取第一个元素 #}
    <p>{{ [1,2,3,4,5] | first }}</p>

    {# 取最后一个元素 #}
    <p>{{ [1,2,3,4,5] | last }}</p>

    {# 返回列表长度,可以写为count #}
    <p>{{ [1,2,3,4,5] | length }}</p>

    {# 列表求和 #}
    <p>{{ [1,2,3,4,5] | sum }}</p>

    {# 列表排序,默认为升序 #}
    <p>{{ [3,2,1,5,4] | sort }}</p>

    {# 合并为字符串,返回"1 | 2 | 3 | 4 | 5" #}
    <p>{{ [1,2,3,4,5] | join('--') }}</p>

    {# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #}
    <p>{{ ['tom','bob','ada'] | upper }}</p>
字典操作
    {{ '字典列表操作...' }}
    {% set users=[{'name':'Tom','gender':'M','age':20},
                  {'name':'John','gender':'M','age':18},
                  {'name':'Mary','gender':'F','age':24},
                  {'name':'Bob','gender':'M','age':31},
                  {'name':'Lisa','gender':'F','age':19}]
    %}

    <div>{{ users }}</div>

    {# 按指定字段排序,这里设reverse为true使其按降序排 #}
    <ul>
    {% for user in users | sort(attribute='age', reverse=true) %}
         <li>{{ user.name }}, {{ user.age }}</li>
    {% endfor %}
    </ul>

    {# 列表分组,每组是一个子列表,组名就是分组项的值 #}
    <ul>
    {% for group in users|groupby('gender') %}
        <li>{{ group.grouper }}<ul>
        {% for user in group.list %}
            <li>{{ user.name }}</li>
        {% endfor %}</ul></li>
    {% endfor %}
    </ul>

    {# 取字典中的某一项组成列表,再将其连接起来 #}
    <p>{{ users | map(attribute='name') | join(', ') }}</p>
语句块过滤
    {% filter upper %}
        This is a Flask Jinja2 introduction.
    {% endfilter %}

自定义过滤器

两种写法

  • 不用装饰器
def double_step_filter(l):
    return l[::2]

app.add_template_filter(double_step_filter, 'double_step')
  • 使用装饰器
@app.template_filter('sub')
def sub(l, start, end):
    return l[start:end]

输出…

    {# 返回[1,3,5] #}
    <p>{{ [1,2,3,4,5] | double_step }}</p>

    {# 返回[2,3,4] #}
    <p>{{ [1,2,3,4,5] | sub(1,4) }}</p>

内置测试器

显然测试器本质上也是一个函数,它的第一个参数就是待测试的变量,在模板中使用时可以省略去。如果它有第二个参数,模板中就必须传进去。测试器函数返回的必须是一个布尔值,这样才可以用来给if语句作判断。

    {% set name = 'Ltf' %}
        <p>{{ name }}</p>
    {# 检查变量是否被定义,也可以用undefined检查是否未被定义 #}
    {% if name is defined %}
        <p>Name is: {{ name }}</p>
    {% endif %}

    {# 检查是否所有字符都是大写 #}
    {% if name is upper %}
        <h2>"{{ name }}" are all upper case.</h2>
    {% endif %}

    {# 检查变量是否为空 #}
    {% if name is none %}
        <h2>Variable is none.</h2>
    {% endif %}

    {# 检查变量是否为字符串,也可以用number检查是否为数值 #}
    {% if name is string %}
        <h2>{{ name }} is a string.</h2>
    {% endif %}

    {# 检查数值是否是偶数,也可以用odd检查是否为奇数 #}
    {% if 2 is even %}
        <h2>Variable is an even number.</h2>
    {% endif %}

    {# 检查变量是否可被迭代循环,也可以用sequence检查是否是序列 #}
    {% if [1,2,3] is iterable %}
        <h2>Variable is iterable.</h2>
    {% endif %}

    {# 检查变量是否是字典 #}
    {% if {'name':'test'} is mapping %}
        <h2>Variable is dict.</h2>
    {% endif %}

自定义测试器

和自定义过滤器的写法一样

  • 无装饰器
import re
def has_number(str):
    return re.match(r'.*\d+', str)
app.add_template_test(has_number,'contain_number')
  • 有装饰器
@app.template_test('end_with')
def end_with(str, suffix):
    return str.lower().endswith(suffix.lower())
  • 模板语句
    {% if name is contain_number %}
        <h2>"{{ name }}" contains number.</h2>
    {% endif %}

    {% if name is end_with "me" %}
        <h2>"{{ name }}" ends with "me".</h2>
    {% endif %}

Github地址

猜你喜欢

转载自blog.csdn.net/ltf201834/article/details/89356684