如何快速掌握[python][flask] Jinja 模板?

摘要:Flask 和 Django 附带了强大的 Jinja 模板语言。 对于之前没有接触过模板语言的人来说,这类语言基本上就是包含一些变量,当准备渲染呈现 HTML 时,它们会被实际的值替换。 这些变量放在标记或分隔符之前。例如:Jinja 模
目录1、快速体验2、Flask 最小 DEMO3、模板继承4、Super Blocks5、Macros6、自定义过滤器7、结论参考链接 Flask 和 Django 附带了强大的 Jinja 模板语言。 对于之前没有接触过模板语言的人来说,这类语言基本上就是包含一些变量,当准备渲染呈现 HTML 时,它们会被实际的值替换。 这些变量放在标记或分隔符之前。例如:Jinja 模板使用 {% ... %} 表示循环,{{ ... }} 表示一个表达式运算结果返回。 Jinja 模板其实是 html 文件。一般情况下放在 Flask 工程的 /templates 目录下 1、快速体验 跑下面的各种 demo 之前,确保你已经安装了 Jinja (pip install jinja2) >>> from jinja2 import Template >>> t = Template("Hello {{ something }}!") >>> t.render(something="World") u'Hello World!' >>> t = Template("My favorite numbers: {% for n in range(1,10) %}{{n}} " "{% endfor %}") >>> t.render() u'My favorite numbers: 1 2 3 4 5 6 7 8 9 ' 这个 demo 展示了模板中的变量(表达式)是如何最终被替换和渲染的。 2、Flask 最小 DEMO 整个的参考代码可以在这里获得:HERE 不过博主建议按照下面步骤一步步来: 1)安装 flask ➜ pip install flask 2)创建工程目录结构: ➜ mkdir flask_example ➜ cd flask_example ➜ mkdir templates ➜ cd .. ➜ touch run.py ➜ touch requirements.txt 3)编写 run.py from flask import Flask, render_template app = Flask(__name__) @app.route("/") def template_test(): return render_template('template.html', my_string="Wheeeee!", my_list=[0,1,2,3,4,5]) if __name__ == '__main__': app.run(debug=True) 这里,我们创建了一个 / 路由,当我们访问服务器根路由时,会通过 render_template 将 template.html 渲染,其中 my_string 和 my_list 就是准备传给模板的实际的值。 4)编写 template.html 模板 在 templates 目录下,创建一个 template.html: <!DOCTYPE html> <html> <head> <title>Flask Template Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" media="screen"> <style type="text/css"> .container { max-width: 500px; padding-top: 100px; } </style> </head> <body> <div class="container"> <p>My string: {{my_string}}</p> <p>Value from the list: {{my_list[3]}}</p> <p>Loop through the list:</p> <ul> {% for n in my_list %} <li>{{n}}</li> {% endfor %} </ul> </div> <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script> <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> </body> </html> 5)运行观察效果 ➜ python run.py 效果如下: 可以看到,将模板中的 my_string、my_list[3] 替换掉了,并且用 for 循环语句,生成了一个 list。 3、模板继承 模板通常利用继承,继承包括定义所有后续子模板基本结构的单个基础模板。您可以使用标记 {% extends %} 和 {% block %} 来实现继承。 这样做的用例很简单:随着应用程序的增长,以及您继续添加新模板,您将需要保持公共代码(如HTML导航栏、Javascript库、CSS样式表等)同步,这可能需要大量工作。使用继承,我们可以将这些公共部分移动到父/基模板,这样我们就可以创建或编辑这样的代码一次,所有子模板都将继承该代码。 注意:您应该总是尽可能多地向基本模板添加重复代码,以节省将来的时间,这将远远超过初始时间投资。 让我们给我们的 DEMO 增加模板: 1)创建基础模板(保存为 layout.html) <!DOCTYPE html> <html> <head> <title>Flask Template Example</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" media="screen"> <style type="text/css"> .container { max-width: 500px; padding-top: 100px; } h2 {color: red;} </style> </head> <body> <div class="container"> <h2>This is part of my base template</h2> <br> {% block content %}{% endblock %} <br> <h2>This is part of my base template</h2> </div> <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script> <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> </body> </html> 你注意到 {%block%} 标记了吗?这定义了子模板可以填充的块或区域。此外,也可实现覆盖的作用。 2)用模板更新 template.html: {% extends "layout.html" %} {% block content %} <h3> This is the start of my child template</h3> <br> <p>My string: {{my_string}}</p> <p>Value from the list: {{my_list[3]}}</p> <p>Loop through the list:</p> <ul> {% for n in my_list %} <li>{{n}}</li> {% endfor %} </ul> <h3> This is the end of my child template</h3> {% endblock %} 这样 layout.html 模板中的 content 块就会被 template.html 中的新定义给替换掉,最终效果如下: 那么,我们就可以通过修改 layout.html 给其添加通用导航栏了:(将下列代码插入到 layout.html 的 <body> 标签之后) <nav class="navbar navbar-inverse" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Jinja!</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link</a></li> <li><a href="#">Link</a></li> </ul> <form class="navbar-form navbar-left" role="search"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> 现在,从基础扩展的每个子模板都将具有相同的导航栏。借用Java哲学的一句话:"Write once, use anywhere." 4、Super Blocks 如果需要从基础模板渲染块,使用 super block: {{ super() }} 给基础模板增加一个页脚: <body> <div class="container"> ... <h2>This is part of my base template</h2> <br> <div class="footer"> {% block footer %} Watch! This will be added to my base and child templates using the super powerful super block! <br> <br> <br> {% endblock %} </div> </div> ... 此时,我们可以给 template.html 增加 super block,从而实现子模板复用父模板中的块: {% extends "layout.html" %} {% block content %} <h3> This is the start of my child template</h3> <br> <p>My string: {{my_string}}</p> <p>Value from the list: {{my_list[3]}}</p> <p>Loop through the list:</p> <ul> {% for n in my_list %} <li>{{n}}</li> {% endfor %} </ul> <h3> This is the end of my child template</h3> {% block footer %} {{super()}} {% endblock %} {% endblock %} 效果如下: super block 用于模块共享父模块的 block,当然还有一些高级玩法,比如下面的例子: 父模板: {% block heading %} <h1>{% block page %}{% endblock %} - Flask Super Example</h1> {% endblock %} 子模板: {% block page %}Home{% endblock %} {% block heading %} {{ super() }} {% endblock %} 这样当访问子模块时,会拼接一个 <h1>Home - Flask Super Example</h1> 字段。发现没,我们通过这样的方法,实现了标题的继承(有一定的继承,也有一定的子模块自己的信息)。 回归正轨,对于更新标题,我们这里这样设计(修改 template.html 中的两行代码) {% block title %}{{title}}{% endblock %} ... {% block page %}{{title}}{% endblock %} 这样我们可以通过 python 进来直接修改标题了(修改 run.py): @app.route("/") def template_test(): return render_template( 'template.html', my_string="Wheeeee!", my_list=[0,1,2,3,4,5], title="Home") 5、Macros 在 Jinja 中,我们可以使用宏来抽象常用的代码段,这些代码段被反复使用以避免重复。例如,通常会在导航栏上突出显示当前页面的链接(活动链接)。否则,我们必须使用 if/elif/else 语句来确定活动链接。使用宏,我们可以将这些代码抽象成一个单独的文件。 新增一个 macros.html 文件: {% macro nav_link(endpoint, name) %} {% if request.endpoint.endswith(endpoint) %} <li class="active"><a href="{{ url_for(endpoint) }}">{{name}}</a></li> {% else %} <li><a href="{{ url_for(endpoint) }}">{{name}}</a></li> {% endif %} {% endmacro %} 这里,我们使用了 Flask 的 request object(Jinja 的默认一部分),用来检查请求端点,然后将活动 class 分配给该端点。 使用基础模板中的nav navbar nav类更新无序列表: <ul class="nav navbar-nav"> {{ nav_link('home', 'Home') }} {{ nav_link('about', 'About') }} {{ nav_link('contact', 'Contact Us') }} </ul> 此外,请确保在模板顶部添加导入:{% from "macros.html" import nav_link with context %} 最后,让我们向控制器添加三个新端点: @app.route("/home") def home(): return render_template( 'template.html', my_string="Wheeeee!", my_list=[0,1,2,3,4,5], title="Home") @app.route("/about") def about(): return render_template( 'template.html', my_string="Wheeeee!", my_list=[0,1,2,3,4,5], title="About") @app.route("/contact") def contact(): return render_template( 'template.html', my_string="Wheeeee!", my_list=[0,1,2,3,4,5], title="Contact Us") 刷新页面。测试顶部的链接。当前页面是否突出显示?(每次点击 Home, About, Contact Us,浏览器会自动跳转到对应的 url,并加载页面) 6、自定义过滤器 Jinja 使用过滤器修改变量,主要用于格式化目的。 这有个例子; {{ num | round }} 这将使 num 变量四舍五入。因此,如果我们将参数 num=46.99 传递到模板中,那么将输出47.0。(把大括号中的语句当做 shell,就明白了,竖线是传递作用,round是个过滤器,这里是所有的过滤器) 再来个例子: {{ list|join(', ') }} 可以给 list 数组中的变量加个逗号。 其实,除了自带的过滤器,我们也可以自定义: 1)在 run.py 的所有函数前增加 app = Flask(__name__) 用于创建一个 app 2)增加一个 datetimefilter 函数,并将其注册到 app 的过滤器 @app.template_filter() # 声明,这是个过滤器 def datetimefilter(value, format='%Y/%m/%d %H:%M'): """Convert a datetime to a different format.""" return value.strftime(format) app.jinja_env.filters['datetimefilter'] = datetimefilter 3)这样,我们在子模板中插入如下代码: <h4>Current date/time: {{ current_time | datetimefilter }}</h4> 4)最后,只要在 python 中将时间传入模板即可: current_time = datetime.datetime.now() 5)效果如下: 7、结论 这样,就送大家快速入门了 Jinja,源码:https://github.com/mjhea0/thinkful-mentor/tree/master/python/jinja/flask_example 参考链接 [1]. 本文源码 [2]. Primer on Jinja Templating(本文翻译并参考这篇) [3]. Flask 官方文档 [4]. 真正搞明白Python中Django和Flask框架的区别 [5]. Flask 主页 [6]. 一个 Soft UI Dashboard - Free Jinja Template [7]. Appseed 这个网站有很多 Flask 模板 [8]. Nginx 服务器 SSL 证书安装部署 [9]. python django web 开发 —— 15分钟送到会用(只能送你到这了) : 在学习 Django 和 Flask 等 Python 的服务器框架时,都需要了解模板的概念,这篇能够快速带你入门...