Flask中的Jinja2模板使用

来源:互联网 发布:手机淘宝卖家怎么激活 编辑:程序博客网 时间:2024/05/17 17:43

模板

@(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-8from flask import Flask, render_templatefrom flask.ext.script import Managerapp = 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 Managerfrom 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 Momentmoment = 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的格式化选项。

1 0
原创粉丝点击