在Python的Flask框架中使用模版的入门教程


Posted in Python onApril 20, 2015

 概述

如果你已经阅读过上一个章节,那么你应该已经完成了充分的准备工作并且创建了一个很简单的具有如下文件结构的Web应用:
 
microblog
    |-flask文件夹
    |-<一些虚拟环境的文件>
    |-app文件夹
    |  |-static文件夹
    |  |-templates文件夹
    |  |-__init__.py文件
    |  |-views.py文件
    |-tmp文件夹
    |-run.py文件

亲,想要运行这个程序么?那就运行这个run.py文件,然后在你的浏览器里边打开http://localhost:5000这个地址.

我们在后面的章节会不断地从前一章节结束的地方继续开发我们的应用,所以你要确保你的环境已经正确安装,并且你的应用能够正常运行.
 

为什么我们需要使用模板系统

让我们来考虑一下,我们接下来怎么样扩展我们的小程序.

我们要在微博应用里边实现一个非常基本的功能,在首页上面显示一个欢迎已登录用户的标题.暂且忽略当前应用里边没有用户的状况,我将会在稍后解决这个问题.

要显示一个美观大方的标题,最简单的方法就是改变我们提供视图的方式来显示一些HTML代码,不如这样子:\
 

from app import app
 
@app.route('/')
@app.route('/index')
def index():
  user = { 'nickname': 'Miguel' } # fake user
  return '''
<html>
 <head>
  <title>Home Page</title>
 </head>
 <body>
  <h1>Hello, ''' + user['nickname'] + '''</h1>
 </body>
</html>

现在,在你的浏览器里面刷新一下看看,是不是很爽?
 

因为我们这个小程序还支持用户功能,所以咱用了一个用户占位对象,通常它被亲切的称呼为假数据或测试数据。它可以让我们关注程序中急需解决的部分。

爽完了,面对实际吧,我希望你会觉得上面这个混合着html代码的小程序相当恶心。想想看,如果你用一些动态内容生成的一个复杂的HTML页面,并且想要在由这样的程序组成的网站中改变一些页面内容,这将会一件非常蛋疼的事情。

模板系统拯救世界

如果保持业务逻辑和表现的分离,你的网站结构将会组织的更好。不要不信,如果你用python完成了业务代码,你甚至可以请一个网站设计师帮你完成剩下的部分。模板系统就是帮你实现业务和表现分离的。

让我们完成第一个模板(fileapp/templates/index.html):
 

<html>
 <head>
  <title>{{title}} - microblog</title>
 </head>
 <body>
   <h1>Hello, {{user.nickname}}!</h1>
 </body>
</html>

如你所见,我们只是写了一个普通的HTML页面,唯一跟HTML有区别的就是里面掺杂了一些以 {{ ... }} 组成的动态内容占位符。

现在让我们来看看视图函数中是如何处理这个模板的(fileapp/views.py):
 

from flask import render_template
from app import app
 
@app.route('/')
@app.route('/index')
def index():
  user = { 'nickname': 'Miguel' } # fake user
  return render_template("index.html",
    title = 'Home',
    user = user)

测试这段程序的重点就是看看模板系统是如何工作的。你可以比较浏览器中渲染后的html页面源码与模板文件源码之间的区别。

在上面的程序中,我们从 Flask 框架 import 了一个叫 render_template 的新函数,并用这个函数来渲染模板。并给这个函数赋予了模板文件名和一些变量作为参数。它将导入的变量替换掉模板中的变量占位符,并返回渲染后的模板。

让我们了解的更深入点。在 Flask 底层,render_template 函数实际上是调用了 Flask 的一个组件: Jinja2 模板处理引擎。是 Jinjia2 用导入的变量替换掉了模板中对应的 {{ ... }} 代码块。

模板中的流程控制

Jinja2 模板系统还支持流程控制语句,我们来尝试一下在模板中添加一个 if 流程控制语句 (fileapp/templates/index.html):
 

<html>
 <head>
  {% if title %}
  <title>{{title}} - microblog</title>
  {% else %}
  <title>Welcome to microblog</title>
  {% endif %}
 </head>
 <body>
   <h1>Hello, {{user.nickname}}!</h1>
 </body>
</html>

现在我们的模板文件有点智能了。如果我们在视图函数中忘了定义页面标题变量 title,它将会使用自已的标题替代。把视图函数中 render_template 里的 title 变量取消试试,看看这个 if 流程语句是如何工作的。

模板中使用循环

也许用户想在他的主页上展示好友最近写的文章,有点像人人,或者新浪微博那样的好友动态,接下来我们就要看看如何来完成这个功能。

首先,创建用户和文章 (fileapp/views.py):
 

def index():
  user = { 'nickname': 'Miguel' } # fake user
  posts = [ # fake array of posts
    {
      'author': { 'nickname': 'John' },
      'body': 'Beautiful day in Portland!'
    },
    {
      'author': { 'nickname': 'Susan' },
      'body': 'The Avengers movie was so cool!'
    }
  ]
  return render_template("index.html",
    title = 'Home',
    user = user,
    posts = posts)

用数组存储用户的文章,每一个数组元素都是一个字典,如上代码所示,这个dict的key是author和body,用来存储文章的作者和文章内容。当我们决定使用数据库来存储这些信息时,这个字典的key可以隐射为表的一个字段,这里为了给大家演示模板的使用,没有使用数据库相关的技术,简单地使用字典和数组模拟用户和他的好友最近发表的文章。
 

我们的模板文件现在有了一个新问题。我们刚刚创建了一个包含用户文章的内容数据,这个数组可能包含任意数量的文章。怎样才能让模板根据这个数组的数量自动渲染内容。

要解决这个问题,就需要一个新的流程控制语句:for循环。让我们来把 for循环添加到模板文件 (fileapp/templates/index.html)
 

<html>
 <head>
  {% if title %}
  <title>{{title}} - microblog</title>
  {% else %}
  <title>microblog</title>
  {% endif %}
 </head>
 <body>
  <h1>Hi, {{user.nickname}}!</h1>
  {% for post in posts %}
  <p>{{post.author.nickname}} says: <b>{{post.body}}</b></p>
  {% endfor %}
 </body>
</html>

很简单吧,你还可以在数组中添加更多的内容看看效果。
 
模板继承

接来下,我们需要给这个微博(microblog)添加一个导航菜单,里面包含 修改个人资料,退出登录 等类似的链接。

直接在 index.html 模板文件中直接加入这个导航条也是可行的,但是当我们有很多模板文件都包含这个导航条时,就会陷入为了修改导航条的某个地方而不得不在多个文件中往返编辑的尴尬境地。当包含这个导航条的文件越来越多时,你想死的心就会有了。

我们可以使用 Jinja2 的模板继承功能,它可以使我们把模板中一些公共的内容组合在一起创建一个基础模板,然后让其它模板继承这个基础模板。

我们先来定义一个基础模板,里面包含了导航条和那个最开始写的关于页面标题(title)的流程控制语句。(fileapp/templates/base.html):
 

<html>
 <head>
  {% if title %}
  <title>{{title}} - microblog</title>
  {% else %}
  <title>microblog</title>
  {% endif %}
 </head>
 <body>
  <div>Microblog: <a href="/index">Home</a></div>
  <hr>
  {% block content %}{% endblock %}
 </body>
</html>

在这个模板中,我们使用了 block 控制语句来定义继承模板内容的显示位置。注意:这个 block 语句中设置的名称必须唯一。

接下来让我们修改一下 index.html 模板,让它继承于刚刚添加的基础模板 base.html (fileapp/templates/index.html):

 

{% extends "base.html" %}
{% block content %}
<h1>Hi, {{user.nickname}}!</h1>
{% for post in posts %}
<div><p>{{post.author.nickname}} says: <b>{{post.body}}</b></p></div>
{% endfor %}
{% endblock %}

基础模板 base.html 帮我们搞定了页面结构和公共内容,所以这个 index.html 模板就成了这幅衰样了。extends 语句使两个模板关联了起来。Jinja2 在渲染 index.html 模板时,发现 extends 语句,就会自动先引入 base.html 基础模板,并对两个模板中名为 content 的 block 语句进行匹配。Jinja2知道如何把两个模板合并到一起。我们以后创建新的模板时,同样也会使用这种从基础模板继承的方法。

结束语

如果你想节省时间,懒得敲代码,可以从以下地址下载本章内容的示例代码:

下载地址:microblog-0.2.zip

Python 相关文章推荐
高效测试用例组织算法pairwise之Python实现方法
Jul 19 Python
Python使用matplotlib绘制余弦的散点图示例
Mar 14 Python
TensorFlow实现简单的CNN的方法
Jul 18 Python
django框架创建应用操作示例
Sep 26 Python
python两个_多个字典合并相加的实例代码
Dec 26 Python
Python 统计位数为偶数的数字代码详解
Mar 15 Python
Python连接Mysql进行增删改查的示例代码
Aug 03 Python
Python 程序员必须掌握的日志记录
Aug 17 Python
Python selenium环境搭建实现过程解析
Sep 08 Python
Python如何获取文件路径/目录
Sep 22 Python
python matplotlib库的基本使用
Sep 23 Python
利用python批量爬取百度任意类别的图片的实现方法
Oct 07 Python
使用Node.js和Socket.IO扩展Django的实时处理功能
Apr 20 #Python
利用Python的Django框架中的ORM建立查询API
Apr 20 #Python
对于Python的框架中一些会话程序的管理
Apr 20 #Python
介绍Python的Django框架中的QuerySets
Apr 20 #Python
使用Python的Django框架实现事务交易管理的教程
Apr 20 #Python
简化Python的Django框架代码的一些示例
Apr 20 #Python
在Python的Django框架上部署ORM库的教程
Apr 20 #Python
You might like
PHP类中Static方法效率测试代码
2010/10/17 PHP
比较strtr, str_replace和preg_replace三个函数的效率
2013/06/26 PHP
深入理解PHP之源码目录结构与功能说明
2016/06/01 PHP
javascript之函数直接量(function(){})()
2007/06/29 Javascript
js+数组实现网页上显示时间/星期几的实用方法
2013/01/18 Javascript
js this函数调用无需再次抓获id,name或标签名
2014/03/03 Javascript
Bootstrap基本插件学习笔记之Popover提示框(19)
2016/12/08 Javascript
js中的DOM模拟购物车功能
2017/03/22 Javascript
Vuex之理解state的用法实例
2017/04/19 Javascript
Require.JS中的几种define定义方式示例
2017/06/01 Javascript
对存在JavaScript隐式类型转换的四种情况的总结(必看篇)
2017/08/31 Javascript
nodejs用gulp管理前端文件方法
2018/06/24 NodeJs
详解Vue中数组和对象更改后视图不刷新的问题
2018/09/21 Javascript
Vue keepAlive 数据缓存工具实现返回上一个页面浏览的位置
2019/05/10 Javascript
layui表格 返回的数据状态异常的解决方法
2019/09/10 Javascript
Vue实现PC端靠边悬浮球的代码
2020/05/09 Javascript
[10:14]2018DOTA2国际邀请赛寻真——paiN Gaming不仅为自己而战
2018/08/14 DOTA
使用Python的urllib2模块处理url和图片的技巧两则
2016/02/18 Python
django模型层(model)进行建表、查询与删除的基础教程
2017/11/21 Python
浅析Python3中的对象垃圾收集机制
2019/06/06 Python
python opencv minAreaRect 生成最小外接矩形的方法
2019/07/01 Python
Python object类中的特殊方法代码讲解
2020/03/06 Python
opencv 查找连通区域 最大面积实例
2020/06/04 Python
Python如何实现Paramiko的二次封装
2021/01/30 Python
利用HTML5+CSS3实现3D转换效果实例详解
2017/05/02 HTML / CSS
Edwaybuy西班牙:小米在线商店
2019/12/04 全球购物
Clos19英国:高档香槟、葡萄酒和烈酒在线购物平台
2020/07/10 全球购物
公司委托书格式范文
2014/04/04 职场文书
询价采购方案
2014/06/09 职场文书
优秀教师自我评价范文
2014/09/27 职场文书
部门2014年度工作总结
2014/11/12 职场文书
学生自我评语
2015/01/04 职场文书
事业单位工作人员年度考核个人总结
2015/02/12 职场文书
母亲节感言
2015/08/03 职场文书
办公室规章制度范本
2015/08/04 职场文书
2016年优秀少先队辅导员事迹材料
2016/02/26 职场文书