首页 > 代码库 > 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>
,就会被渲染为<h1>Hello</h1>
,这就导致内嵌标签失效,如果需要显示内嵌标签,就用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引擎