首页 > 代码库 > Flask Web Development - Flask 模板1 - 模板机制&Jinja2引擎

Flask Web Development - Flask 模板1 - 模板机制&Jinja2引擎

节选自PartI Chapter3,这个chapter主要讲模板工作原理,这里讲的就是Jinja2这个模板,另外还提到了Flask-Bootstrap及Flask-Moment两个插件,前者对Flask使用Bootstrap做了些封装,后者对moment.js做了些封装。内容较多,估计分开搞。

模板存在的意义

可维护性高的代码是结构良好且整洁的。

当用户在网站注册一个账户时,他在表单里填入邮箱跟密码,并点击提交按钮。在server端就收到一个包含这些数据的request,再由Flask分发到相应的视图函数处理。视图函数这里就产生两个任务,首先在数据库增加新用户,然后给用户的浏览器返回一个response。这两个任务可分别称为业务逻辑(business logic)和报告逻辑(presentation logic)。

将这两个逻辑混在一起可能导致代码混乱,难以维护。可以想象在代码里直接生成复杂HTML页面再嵌入数据的复杂度。将报告逻辑移入模板处理,可以提高应用的可维护性。

模板其实就是一个文本,里面有一些包含变量的动态部分,这些变量的内容在有request上下文时才能知道。将这些变量替换成实际值,最终生成一个response字符串的过程叫渲染。Flask使用强大的Jinja2来处理渲染模板任务。

Jinja2模板引擎

一个简单的Jinja2模板,index.html

<h1>Hello World!</h1>

就是一个固定的字符串,直接返回这个字符串。再看稍复杂的, user.html:

<h1>Hello, {{ name }}!</h1>

里面带有一个动态部分,name是个变量,response会随之改变。

渲染模板

Flask默认到应用根目录下的templates文件夹下找相应模板,先把上面的两个简单模板放到这个文件夹下,然后修改之前写的hello.py

from flask import Flask, render_template
# ...
@app.route(‘/index‘)
def index():
    return render_template(‘index.html‘)
@app.route(‘/user/<name>‘)
def user(name):
    return render_template(‘user.html‘, name=name)

注:上面是github上的3a tag下代码

Flask提供的render_template函数可以配合Jinja2使用,它的第一个参数是模板名,之后的参数都是模板中的变量值,以key/value形式传入。key就是在模板中写的变量名。

模板中的变量

{{ name }}这种结构表示引用了一个变量,它告诉模板引擎,里面的值需要在模板被渲染时提供的数据中获取。

Jinja2可以识别多种类型,甚至包括列表,字典和对象。如:

<p>A value from a dictionary: {{ mydict[‘key‘] }}.</p>
<p>A value from a list: {{ mylist[3] }}.</p>
<p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value from an object‘s method: {{ myobj.somemethod() }}.</p>

变量可以通过过滤器进一步修改,过滤器放在变量后面以分隔符隔开。比如下面模板表示变量被首字母大写化:

Hello, {{ name|capitalize }}

常用过滤器如下:


  • safe 不对传入值转义
  • capitalize 传入值首字母大写,其他小写
  • lower 传入值全部转为大写
  • upper 传入值全部转为小写
  • title 传入值中每个词首字母大写
  • trim 传入值前后空格删除
  • striptags 传入值内所有HTML标签删除

重点提下safe,出于安全性考虑,Jinja2默认把变量中的值进行转义,比如值为<h1>Hello</h1>,就会被渲染为&lt;h1&gt;Hello&lt;/h1&gt;,这就导致内嵌标签失效,如果需要显示内嵌标签,就用safe过滤器。

注:对于无法信任的位置,不要使用safe,比如用户填入的表单值。

更多过滤器可以参考Jinja2官方文档

控制结构

Jinja2提供了几种控制结构,来改变模板的渲染流程。下面介绍几种比较常用的。

模板中加入条件控制:

{% if user %}
    Hello, {{ user }}!
{% else %}
    Hello, Stranger!
{% endif %}

模板中对一个列表元素进行循环:

<ul>
    {% for comment in comments %}
        <li>{{ comment }}</li>
    {% endfor %}
</ul>

Jinja2支持宏,类似于Pyhton代码里的函数:

{% macro render_comment(comment) %}
    <li>{{ comment }}</li>
{% endmacro %}
<ul>
    {% for comment in comments %}
        {{ render_comment(comment) }}
    {% endfor %}
</ul>

想增加宏的重用性,可以把宏放入单独文件中,然后在模板中引入:

{% import ‘macros.html‘ as macros %}
<ul>
    {% for comment in comments %}
        {{ macros.render_comment(comment) }}
    {% endfor %}
</ul>

模板中的一些代码在不同地方重复出现的话,把这部分单独保存成一个文件,在需要的地方include进来,避免重复书写:

{% include ‘common.html‘ %}

增加重用性的另一个重要方式,是模板继承,类似于Python代码中类的继承。先定义一个基本模板:

`base.html`:

<html>
<head>
    {% block head %}
    <title>{% block title %}{% endblock %} - My Application</title>
    {% endblock %}
</head>
<body>
    {% block body %}
    {% endblock %}
</body>
</html>

block标签中的元素可以在继承的模板中被改变。上面出现了名为head,title,body的区块。注意title被定义在了head中。看一个继承这个模板的例子:

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style>
    </style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}

extends关键字声明这个模板是继承自base.html,之后是三个新定义的区块,区块名与定义顺序对应于基本模板。注意在基本模板中head区块是有内容的,使用super()方法可以保留原有内容。

后续会有更多关于模板的真实使用方式。

Flask Web Development - Flask 模板1 - 模板机制&Jinja2引擎