python模板入门教程之flask Jinja


Posted in Python onApril 11, 2022

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_templatetemplate.html 渲染,其中 my_stringmy_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

效果如下:

python模板入门教程之flask Jinja

可以看到,将模板中的 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 中的新定义给替换掉,最终效果如下:

python模板入门教程之flask Jinja

那么,我们就可以通过修改 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."

python模板入门教程之flask Jinja

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 %}

效果如下:

python模板入门教程之flask Jinja

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)效果如下:

python模板入门教程之flask Jinja

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分钟送到会用(只能送你到这了)

Python 相关文章推荐
selenium + python 获取table数据的示例讲解
Oct 13 Python
pandas如何处理缺失值
Jul 31 Python
利用python在大量数据文件下删除某一行的例子
Aug 21 Python
python openCV获取人脸部分并存储功能
Aug 28 Python
Python进程间通信 multiProcessing Queue队列实现详解
Sep 23 Python
Python pip 安装与使用(安装、更新、删除)
Oct 06 Python
Python实现RGB与HSI颜色空间的互换方式
Nov 27 Python
Python+opencv+pyaudio实现带声音屏幕录制
Dec 23 Python
详解Python的三种拷贝方式
Feb 11 Python
Python greenlet和gevent使用代码示例解析
Apr 01 Python
Python中的特殊方法以及应用详解
Sep 20 Python
python3中TQDM库安装及使用详解
Nov 18 Python
使用Python解决图表与画布的间距问题
Python的property属性详细讲解
Apr 11 #Python
OpenCV项目实践之停车场车位实时检测
Python进程池与进程锁之语法学习
Python进程间的通信之语法学习
Python+Matplotlib图像上指定坐标的位置添加文本标签与注释
浅析Python OpenCV三种滤镜效果
You might like
PHP开发文件系统实例讲解
2006/10/09 PHP
Mysql的常用命令
2006/10/09 PHP
php数组排序usort、uksort与sort函数用法
2014/11/17 PHP
ThinkPHP实现动态包含文件的方法
2014/11/29 PHP
php版微信公众账号第三方管理工具开发简明教程
2016/09/23 PHP
php中加密解密DES类的简单使用方法示例
2020/03/26 PHP
jquery 隐藏与显示tr标签示例代码
2014/06/06 Javascript
Mvc提交表单的四种方法全程详解
2016/08/10 Javascript
Bootstrap页面标题Page Header的实现方法
2017/03/22 Javascript
vue-router配合ElementUI实现导航的实例
2018/02/11 Javascript
浅析Vue 生命周期
2018/06/21 Javascript
对layui中表单元素的使用详解
2018/08/15 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
浅谈对于react-thunk中间件的简单理解
2019/05/01 Javascript
解决echarts vue数据更新,视图不更新问题(echarts嵌在vue弹框中)
2020/07/20 Javascript
对于Python的框架中一些会话程序的管理
2015/04/20 Python
Python应用03 使用PyQT制作视频播放器实例
2016/12/07 Python
利用python获取Ping结果示例代码
2017/07/06 Python
django2+uwsgi+nginx上线部署到服务器Ubuntu16.04
2018/06/26 Python
Python3中列表list合并的四种方法
2019/04/19 Python
python安装numpy和pandas的方法步骤
2019/05/27 Python
python实现根据文件格式分类
2019/10/31 Python
python使用html2text库实现从HTML转markdown的方法详解
2020/02/21 Python
python matplotlib包图像配色方案分享
2020/03/14 Python
用python对excel进行操作(读,写,修改)
2020/12/25 Python
css3实现3d旋转动画特效
2015/03/10 HTML / CSS
让IE下支持Html5的placeholder属性的插件
2014/09/02 HTML / CSS
Carolina工作鞋官网:Carolina Footwear
2019/03/14 全球购物
安全教育演讲稿
2014/05/09 职场文书
温馨提示标语
2014/06/26 职场文书
大学生撤销处分思想汇报
2014/09/12 职场文书
团委工作总结2015
2015/04/02 职场文书
nginx里的rewrite跳转的实现
2021/03/31 Servers
SQL写法--行行比较
2021/08/23 SQL Server
SSM项目使用拦截器实现登录验证功能
2022/01/22 Java/Android
sql注入报错之注入原理实例解析
2022/06/10 MySQL