在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 相关文章推荐
CentOS 7下Python 2.7升级至Python3.6.1的实战教程
Jul 06 Python
浅析python中的迭代与迭代对象
Oct 08 Python
PyGame贪吃蛇的实现代码示例
Nov 21 Python
Python实现的删除重复文件或图片功能示例【去重】
Apr 23 Python
python算法题 链表反转详解
Jul 02 Python
Python列表与元组的异同详解
Jul 02 Python
如何使用python3获取当前路径及os.path.dirname的使用
Dec 13 Python
python kafka 多线程消费者&amp;手动提交实例
Dec 21 Python
python实现ip地址的包含关系判断
Feb 07 Python
Python实现鼠标自动在屏幕上随机移动功能
Mar 14 Python
Python判断字符串是否为合法标示符操作
Sep 03 Python
python实现腾讯滑块验证码识别
Apr 27 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 simplexmlElement操作xml的命名空间实现代码
2011/01/04 PHP
php修改指定文件后缀的方法
2014/09/11 PHP
php实现refresh刷新页面批量导入数据的方法
2014/12/23 PHP
php 的反射详解及示例代码
2016/08/25 PHP
PHP创建单例后台进程的方法示例
2017/05/23 PHP
别了 JavaScript中的isXX系列
2012/08/01 Javascript
javascript中Number对象的toString()方法分析
2014/12/20 Javascript
JavaScript事件委托技术实例分析
2015/02/06 Javascript
JS中完美兼容各大浏览器的scrolltop方法
2015/04/17 Javascript
js简单实现标签云效果实例
2015/08/06 Javascript
js实现动态加载脚本的方法实例汇总
2015/11/02 Javascript
js判断出两个字符串最大子串的函数实现方法
2016/11/01 Javascript
微信小程序实现图片上传功能
2018/05/28 Javascript
JavaScript链式调用实例浅析
2018/12/19 Javascript
模块化react-router配置方法详解
2019/06/03 Javascript
vue 自动化路由实现代码
2019/09/03 Javascript
layui 弹出删除确认界面的实例
2019/09/06 Javascript
Python脚本在Appium库上对移动应用实现自动化测试
2015/04/17 Python
python删除特定文件的方法
2015/07/30 Python
python中的插值 scipy-interp的实现代码
2018/07/23 Python
详解python中的hashlib模块的使用
2019/04/22 Python
python实现DEM数据的阴影生成的方法
2019/07/23 Python
Python分割训练集和测试集的方法示例
2019/09/19 Python
wxPython实现带颜色的进度条
2019/11/19 Python
pyecharts动态轨迹图的实现示例
2020/04/17 Python
python中Ansible模块的Playbook的具体使用
2020/05/28 Python
python基于socket模拟实现ssh远程执行命令
2020/12/05 Python
纯CSS和jQuery实现的在页面顶部显示的进度条效果2例(仿手机浏览器进度条效果)
2014/04/16 HTML / CSS
AJAX的全称是什么
2012/11/06 面试题
工程现场管理求职自荐信
2013/10/02 职场文书
学期自我评价
2014/01/27 职场文书
2014年百日安全生产活动总结
2014/05/04 职场文书
办理护照工作证明
2014/10/10 职场文书
钳工实训报告总结
2014/11/04 职场文书
教师廉洁自律个人总结
2015/02/10 职场文书
岗位聘任协议书
2015/09/21 职场文书