在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 相关文章推荐
python实现自动发送邮件
Jun 20 Python
基于DataFrame改变列类型的方法
Jul 25 Python
Pycharm无法使用已经安装Selenium的解决方法
Oct 13 Python
使用python写的opencv实时监测和解析二维码和条形码
Aug 14 Python
Python 等分切分数据及规则命名的实例代码
Aug 16 Python
python 图像的离散傅立叶变换实例
Jan 02 Python
浅谈keras 模型用于预测时的注意事项
Jun 27 Python
python3爬虫中异步协程的用法
Jul 10 Python
浅析Python 抽象工厂模式的优缺点
Jul 13 Python
Python+Opencv身份证号码区域提取及识别实现
Aug 25 Python
Python-typing: 类型标注与支持 Any类型详解
May 10 Python
Python+Tkinter制作专属图形化界面
Apr 01 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 foreach循环使用详解与实例代码
2010/05/08 PHP
配置php.ini实现PHP文件上传功能
2014/11/27 PHP
详谈PHP面向对象中常用的关键字和魔术方法
2017/02/04 PHP
jQuery源码分析-01总体架构分析
2011/11/14 Javascript
jquery中获取id值方法小结
2013/09/22 Javascript
jquery给图片添加鼠标经过时的边框效果
2013/11/12 Javascript
让jQuery与其他JavaScript库并存避免冲突的方法
2013/12/23 Javascript
Node.js中使用Log.io在浏览器中实时监控日志(等同tail -f命令)
2014/09/17 Javascript
微信小程序 网络API Websocket详解
2016/11/09 Javascript
jQuery动态生成不规则表格(前后端)
2017/02/21 Javascript
nodejs爬虫遇到的乱码问题汇总
2017/04/07 NodeJs
Vue键盘事件用法总结
2017/04/18 Javascript
vue-cli入门之项目结构分析
2017/04/20 Javascript
mpvue跳转页面及注意事项
2018/08/03 Javascript
解决vue的变量在settimeout内部效果失效的问题
2018/08/30 Javascript
vue 循环加载数据并获取第一条记录的方法
2018/09/26 Javascript
webpack HappyPack实战详解
2019/10/08 Javascript
vue使用自定义事件的表单输入组件用法详解【日期组件与货币组件】
2020/06/01 Javascript
原生js+canvas实现贪吃蛇效果
2020/08/02 Javascript
openlayers实现地图测距测面
2020/09/25 Javascript
跟老齐学Python之用Python计算
2014/09/12 Python
Python写入数据到MP3文件中的方法
2015/07/10 Python
python最长回文串算法
2018/06/04 Python
python实现矩阵打印
2019/03/02 Python
python使用pandas处理大数据节省内存技巧(推荐)
2019/05/05 Python
python实现五子棋游戏
2019/06/18 Python
Python 3.6 -win64环境安装PIL模块的教程
2019/06/20 Python
Python Web框架之Django框架cookie和session用法分析
2019/08/16 Python
关于sys.stdout和print的区别详解
2019/12/05 Python
python使用正则来处理各种匹配问题
2019/12/22 Python
用python写爬虫简单吗
2020/07/28 Python
说出一些常用的类,包,接口
2014/09/22 面试题
模范教师事迹材料
2014/02/10 职场文书
12.4全国法制宣传日活动方案
2014/11/02 职场文书
职代会闭幕词
2015/01/28 职场文书
公司管理建议书
2015/09/14 职场文书