Flask中的Jinja2模板使用

本文详细介绍了Flask中Jinja2模板的使用,包括渲染模板、模板变量、过滤器、控制结构、模板继承、Flask-Bootstrap集成Twitter Bootstrap、自定义错误页面以及url_for()函数的用法。强调了模板引擎在处理业务逻辑和表现逻辑分离中的作用,同时展示了如何利用过滤器修改变量、使用控制结构和模板继承提高代码复用性。

模板

@(Flask)

视图函数

作用:生成请求响应

一般情况下,请求会改变程序的状态,这种改变相应的需要在视图函数中产生。

两种逻辑的处理

  • 业务逻辑
  • 表现逻辑

两种逻辑不分离将会使得代码难以理解和维护。

Jinja2模板引擎

模板是包含响应文本的文件。

动态部分用占位变量表示,具体的值在请求的上下文中才能知道。

渲染:使用真实值替代变量,返回最终的响应字符串。

Flask用的是强大的Jinja2模板引擎。

最简单的模板
  • 只包含响应文本的文件。
#templates/index.html : Jinja2模板

<h1>Hello World!</h1>
  • 添加动态变量
#templates/index.html : Jinja2模板
<h1>Hello,{{ name }}!</h1>

可以看到:Jinja2模板的动态部分的替代和视图函数中的不同。

#视图函数
return '<h1>Hello, %s!</h1>' %name
#Jinja2模板
<h1>Hello,{{ name }}!</h1>

渲染模板

在程序中新建templates文件夹。

新建模板(xxx.html文档)

#index.html
<h1>Hello World!</h1>

#user.html
<h1>Hello, {{ name }}!</h1>

在hello.py中渲染:

#!/usr/bin/python
# coding=utf-8

from flask import Flask, render_template
from flask.ext.script import Manager

app = Flask(__name__)
manager = Manager(app)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/user/<name>')
def user(name):
    return render_template('user.html',name=name)

if __name__ == '__main__':
    manager.run() #服务器运行要依赖manager来管理,有了新的boss

注意引入render_template包,然后视图函数的返回值用render_template()函数来渲染模板,看到渲染时动态传入参数的方法。

render_template()函数

第一个参数是模板的文件名,随后参数都是键值对

name=name这个语法,左边的是模板中的占位变量,右边的是传入参数。

模板变量

此类结构:{{ 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 a object's method: {{ myobj.somemethod() }}</p>
过滤器

使用过滤器修改变量,过滤器名在变量名后,中间用竖线分隔

Hello, {{ name|capitalize }}

这样将会以首字母大写形式显示name的值。

常用的过滤器:

  • safe : 渲染时不转义
  • capitalize : 值的首字母转换为大写,其他字母小写
  • lower : 值转换为小写形式
  • upper : 值转换为大写形式
  • title : 值中的每个单词的首字母转换为大写
  • trim : 值的首尾空格去掉
  • striptags : 渲染之前把值中所有的HTML标签删掉

特别注意safe这个的使用场景,默认情况下,Jinja2会转义所有的变量。

我们有时需要不转义的HTML代码,此时需要safe过滤器。

不要在不可信的值上使用safe过滤器

完整过滤器列表.

控制结构

渲染流程需要引入控制结构。

条件控制

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

渲染一组元素

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

{% 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 'common.html' %>

模板继承

<!DOCTYPE html>
<html>
<head>
    <% block head %>
    <title>{% block title %} {% endblock %}</title>
    <% endblock %>
</head>
<body>
    <% block body %>
    <% endblock %>
</body>
</html>

其中block标签定义的元素可以在衍生模板中修改

扩展语法:

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

通过extends可以达到重复利用模板。

使用Flask-Bootstrap集成Twitter Bootstrap

Bootstrap是客户端框架,主要提供CSS,JS文件,在JS代码宏实例化所需要的组件,这些操作的理想场所是模板。

程序中集成Bootstrap,需要对模板做必要的改动。

更简单的做法是:使用Flask-Bootstrap扩展。

$ pip install flask-bootstrap

修正: 新版的Flask导入包是:

from flask_script import Manager
from flask_bootstrap import Bootstrap

而以前的flask.ext.xx被弃用了,新版的命名空间是flask_xx

使用步骤

基于模板继承机制,使用一个包含所有Bootstrap文件的基模板。

{% extends "bootstrap/base.html" %}

Jinja2的extends指令从Flask-Bootstrap中导入bootstrap/base.html,实现了模板继承。

base.html提供了一个网页框架,引入了Bootstrap中的所有CSS和JS文件。

此外,基模板中定义了可在衍生模板中重新定义的块。

bootstrap/base.html中的块
  • doc : 整个HTML文档
  • html_attribs : <html>标签的属性
  • html : <html>标签中的内容
  • head : <head>标签中的内容
  • title : <title>标签中的内容
  • metas : 一组<meta>标签
  • styles : CSS定义
  • body_attribs : <body>标签的属性
  • body : <body>标签中的内容
  • navbar : 用户定义的导航条
  • content : 用户定义的页面内容
  • scripts : 文档底部的JS声明

直接重定义块会导致问题,因此,如果程序需要向已有内容的块中添加新内容,必须使用super()函数。

示例:添加新的jS文件

{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}

补充:bootstrap/base.html概览

{% block doc -%}
<!DOCTYPE html>
<html{% block html_attribs %}{% endblock html_attribs %}>
{%- block html %}
  <head>
    {%- block head %}
    <title>{% block title %}{% endblock title %}</title>

    {%- block metas %}
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    {%- endblock metas %}

    {%- block styles %}
    <!-- Bootstrap -->
    <link href="{{bootstrap_find_resource('css/bootstrap.css', cdn='bootstrap')}}" rel="stylesheet">
    {%- endblock styles %}
    {%- endblock head %}
  </head>
  <body{% block body_attribs %}{% endblock body_attribs %}>
    {% block body -%}
    {% block navbar %}
    {%- endblock navbar %}
    {% block content -%}
    {%- endblock content %}

    {% block scripts %}
    <script src="{{bootstrap_find_resource('jquery.js', cdn='jquery')}}"></script>
    <script src="{{bootstrap_find_resource('js/bootstrap.js', cdn='bootstrap')}}"></script>
    {%- endblock scripts %}
    {%- endblock body %}
  </body>
{%- endblock html %}
</html>
{% endblock doc -%}

自定义错误页面

场景:在浏览器地址栏输入了不可用的路由,则会有一个状态码为404的错误页面。为了风格一致,需要自定义一个界面。

两个错误代码返回值:
+ 404 : 客户端请求未知页面或路由
+ 500 :有未处理的异常

#异常处理
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'),404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html'),500

可见,也是返回渲染出来的页面,而这些待渲染的页面模板就是我们需要编写的。

问题在于:手工编写会带来许多重复劳动,如何解决这个问题?

模板继承机制

我们在templates/base.html中写成下面这个样子:

{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
         <div class="container">
             <div class="navbar-header">
                 <button type="button" class="navbar-toggle"
                  data-toggle="collapse" data-target=".navbar-collapse">
                     <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="/">Flasky</a>
             </div>
             <div class="navbar-collapse collapse">
                 <ul class="nav navbar-nav">
                     <li><a href="/">Home</a></li>
                 </ul>
             </div>
         </div>
</div>
{% endblock %}
{% block content %} 
    <div class="container">
        {% block page_content %}{% endblock %} 
    </div>
{% endblock %}

异常渲染界面基于这个基模板来写,而不是基于bootstrap的基模板来做。

于是,404.html写作如下:

{% extends "base.html" %}
{% block title %} Flasky -- Page Not Found 
{% endblock %}
{% block page_content %}
<div class="page-header">
    <h1> Not Found</h1>
</div>
{% endblock %}

链接

任何具有多个路由的程序需要可以连接不同页面的链接。

模板中直接编写简单路由的URL链接不难,但是对于包含可变部分的动态路由,在模板中构建正确的URL就困难。此外,直接编写URL会对代码中定义的路由产生不必要的依赖关系。重新定义路由,模板中的连接就会失效。

这个更通俗的解释是:在html中写链接是很容易做到的,但是,这是一种硬编码的做法,更好的做法是通过辅助函数来构造,能够解构出层次来,模块化编写程序。

url_for()辅助函数,使用程序URL映射中保存的信息生成URL。

url_for()函数的用法

最简单的用法是:url_for()以视图函数名或者app.add_url_route()定义路由时使用的端点名作为参数,返回的是对应的URL。

e.g.

url_for('index')返回的是'/'.

url_for('index',_external=True)返回的是绝对地址,即地址栏中的全部地址字符串。

两种运用场景:

  • 程序内部使用相对地址即可
  • 程序外部使用绝对地址

注意拿到相对地址是相对于根目录的。

url_for()使用动态参数

将动态部分作为关键字参数传入,返回的是值替换以后的字符串。

url_for()传递额外的参数

此函数的参数关键字不仅限于动态路由中的参数,还能将任何额外参数添加到查询字符串中。这是个很有用的特性。

url_for('index',page=2)

返回结果就是/?page=2.

静态文件

Web程序不仅仅由Python代码和模板组成,大多数场景下会用到静态文件。
如:

  • 图片
  • JS源代码
  • CSS

URL映射中有一个static路由,对静态文件的引用当做一个特殊的路由。

/static/<filename>

利用:

url_for('static', filename='css/style.css',_external=True)

得到的结果是:http://localhost:5000/static/css/styles.css

默认设置下,Flask在程序根目录中名为static的子目录中寻找静态文件。

Flask-Moment本地化日期和时间

服务器可以使用UTC时间来统一显示,但是UTC显示时间的格式不够友好,会让人感到困惑,更好的方式是采用当地惯用时间格式。

解决方案是:把时间单位发送给Web浏览器转换为当地时间,服务器上只用UTC时间。

moment.js是一个很棒的库,在Flask中可以用Flask-Moment来集成到Jinja2中。

安装

pip install flask-moment

使用语法

from flask_moment import Moment
moment = Moment(app)

除了moment.js库,Flask-Moment 还依赖jquery库。

因此需要在HTML文档的某个地方引入这两个库,可直接引入:

<script src="{{bootstrap_find_resource('js/bootstrap.js', cdn='bootstrap')}}"></script>

也可使用扩展提供辅助函数

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }} 
{% endblock %}

在Bootstrap框架下,已经引入了JQuery,所以只需要再引入moment.js即可。

Flask-Moment向模板开放了moment类,所以在模板中可以调用moment的方法进行渲染。

<!--templates/index.html-->
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>

在hello.py中:

from datetime import datetime
@app.route('/')
def index():
    return render_template('index.html',current_time=datetime.utcnow())

但是实际测试未通过。

函数的使用:

  • format()
  • fromNow()
  • fromTime()
  • calender()
  • valueOf()
  • unix()

具体可查阅文档来学习moment.js的格式化选项。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值