flask web开发实战 入门 pdf Flask Web开发:基于Python的Web应用开发实战



文章插图
flask web开发实战 入门 pdf Flask Web开发:基于Python的Web应用开发实战

文章插图
一、概述
在第二部分中 , 我将讨论如何使用模板 。
学习完第一章之后 , 你已经拥有了一个虽然简单 , 但是可以成功运行Web应用 , 它的文件结构如下:
microblogvenvapp__init__.pyroutes.pymicroblog.py在终端会话中设置环境变量FLASK_APP=microblog.py , 然后执行flask run命令来运行应用 。包含这个应用的Web服务启动之后 , 你可以通过在Web浏览器的地址栏中键入URL http://localhost:5000/ 来验证 。
本章将沿用这个应用 , 在此之上 , 你将学习如何生成包含复杂结构和诸多动态组件的网页 。如果对这个应用和相关开发流程有所遗忘 , 请回顾第一章 。
二、什么是模板?
我设计的微博应用程序的主页会有一个欢迎用户的标题 。虽然目前的应用程序还没有实现用户概念 , 但这不妨碍我使用一个Python字典来模拟一个用户 , 如下所示:
user = {'username':'Miguel'}创建模拟对象是一项实用的技术 , 它可以让你专注于应用程序的一部分 , 而无需为系统中尚不存在的其他部分分心 。在设计应用程序主页的时候 , 我可不希望因为没有一个用户系统来分散我的注意力 , 因此我使用了模拟用户对象 , 来继续接下来的工作 。
原先的视图函数返回简单的字符串 , 我现在要将其扩展为包含完整HTML页面元素的字符串 , 如下所示:
from app import app @app.route('/')@app.route('/index')def index():user = {'username': 'Miguel'}return '''<html><head><title>Home Page - Microblog</title></head><body><h1>Hello, ''' + user['username'] + '''!</h1></body></html>运行flask run 更新视图功能 , 并查看应用程序在浏览器中的显示效果 。
利用上述的代码更新这个视图函数 , 然后再次在浏览器打开它的URL看看结果 。
如果我说这个函数返回HTML的方式并不友好的话 , 你可能会觉得诧异 。设想一下 , 当这个视图函数中的用户和博客不断变化时 , 里面的代码将会变得多么的复杂 。应用的视图函数及其关联的URL也会持续增长 。如果哪天我决定更改这个应用的布局 , 那就不得不更新每个视图函数的HTML字符串 。显然 , 随着应用的扩张 , 这种方式完全不可行 。
将应用程序的后台逻辑和网页布局划分开来 , 你不觉得更容易组织管理吗?甚至你可以聘请一位Web设计师来设计一个杀手级的网站前端 , 而你只需要用Python编写后台应用逻辑 。
模板有助于实现页面展现和业务逻辑之间的分离 。在Flask中 , 模板被编写为单独的文件 , 存储在应用程序包内的templates文件夹中 。在确定你在microblog目录后 , 创建一个存储模板的目录:
在Flask中 , 模板是作为单独的文件编写的 , 存放在应用程序 包内的 templates文件夹(约定俗成命名为 templates)下 。
即在app目录下创建templates文件夹:
# mkdir templates在下面可以看到你的第一个模板 , 它的功能与上面的index()视图函数返回的HTML页面相似 。把这个文件写在app/templates/index.html中:
<html><head><title>{{ title }} - Microblog</title></head><body><h1>Hello, {{ user.username }}!</h1></body></html>这是一个标准的、简单的HTML页面 。但有一点跟我们写HTML代码不同是title标签、h1标签中的 {{ … }}两对花括号 , 在此它是个 占位符 , 作用是将表达式(如文字、数学式子、比较运算符等 , 其实在Python中是一个Python语句)打印到模板进行输出 。具体参考Jinja2官网文档 。这些占位符表示HTML页面中可变的部分 , 并且只在运行时才知道 。
这个HTML页面看起来非常简单 , 唯一值得关注的地方是{{ … }} 。{{ … }}包含的内容是动态的 , 只有在运行时才知道具体表示成什么样子 。
网页渲染转移到HTML模板之后 , 视图函数就能被简化:现在HTML页面的呈现已在HTML模板中了 , 这样接着就可简化一下视图函数index()了 。修改routes.py文件app/routes.py:使用render_template()函数
from app import appfrom flask import render_template#从flask包中导入render_template函数@app.route('/')@app.route('/index')def index():user = {'username':'Miguel'}return render_template('index.html', title='Home', user=user)将模板(index.html)转换为完整HTML页面的操作称之为 呈现(render , 译作 递交、表达、给予 , 在此译作 “渲染”) 。为了渲染模板 , 由从flask包中导入的render_template()完成 , 此函数“携带”模板文件名(index.html)、模板参数的变量列表 , 并返回相同的模板 , 不过其中所有占位符都替换为实际值 。
看起来好多了吧? 赶紧试试这个新版本的应用程序 , 看看模板是如何工作的 。在浏览器中加载页面后 , 你需要从浏览器查看HTML源代码并将其与原始模板进行比较 。
render_template()函数调用Flask框架原生依赖的Jinja2模板引擎 。Jinja2用render_template()函数传入的参数中的相应值替换{{…}}块 。
控制结构—-if条件、for循环、继承
上述过程只是理解了:Jinja2模板引擎 如何在渲染过程中用实际值 替换 (模板中的)占位符 。接下来将认识到更多 Jinja2在模板文件中支持的更多强大操作:if条件、for循环、继承等 。源自官网的这句话:
There are a few kinds of delimiters. The default Jinja delimiters are configured as follows: {% ... %} for Statements{{ ... }} for Expressions to print to the template output{# ... #} for Comments not included in the template output#... ## for Line Statementsif、for、继承均在{% … %}块中写控制语句 。if条件形如:
{% if title %}...语句块{% else %}...语句块{% endif %}【flask web开发实战 入门 pdf Flask Web开发:基于Python的Web应用开发实战】app/templates/index.html:模板中添加条件语句
<html><head>{% if title %}<title>{{ title }} - Microblog</title>{% else %}<title>Welcome to Microblog!</title>{% endif %}</head><body><h1>Hello,{{ user.username }}!</h1></body></html>上述代码中if语句块的功能是:若视图函数index()没有传递title占位符变量的值 , 则index.html模板将会提供默认值(else语句块中) , 而不是显示空标题 。尝试将routes.py中render_template()中的title=’Home’,删除 。效果:图略
for循环在模板中形如:
{% for post in posts %}...语句块{% endfor %}需求:登录用户可在主页中查看最新的帖子 。实现:首先 , 用虚拟对象的方法来创建一些用户、帖子 , 以供显示 。app/routes.py:视图函数中的假帖子
from app import appfrom flask import render_template#从flask包中导入render_template函数 @app.route('/')@app.route('/index')def index():user = {'username':'Miguel'}#用户posts = [#创建一个列表:帖子 。里面元素是两个字典 , 每个字典里元素还是字典 , 分别作者、帖子内容 。{'author': {'username':'John'},'body':'Beautiful day in Portland!'} , {'author': {'username':'Susan'},'body':'The Avengers movie was so cool!'} ]return render_template('index.html', title='Home', user=user, posts=posts)帖子列表 可包含任意数量的元素 , 由视图函数index()决定将在页面中显示的帖子数量 。而模板index.html不能假设这有多少个帖子 , 因此它需要准备好以通用方式呈现视图发送来的尽可能多的帖子 。在模板index.html中 , 用for循环遍历所有的帖子并呈现 。app/templates/index.html:在模板中的for循环
<html><head>{% if title %}<title>{{ title }} - Microblog</title>{% else %}<title>Welcome to Microblog!</title>{% endif %}</head><body><h1>Hello,{{ user.username }}!</h1>{% for post in posts %}<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>{% endfor %}</body></html>模板继承形如:
{% extends "base.html" %} {% block content %}...{% endblock %}现在 , 大部分Web应用程序在页面顶部有一个导航栏 , 它常包含一些常用链接:如登录、退出、编辑个人资料等 。可以很轻松地将导航栏添加到index.html模板 , 甚至更多的HTML页面中 。但随着应用程序的增长(页面数量的增多) , 这些页面都将使用相同的导航栏 , 不可能每一个页面都增加一份相同的导航栏代码 。
Jinja2具有模板继承功能 , 完美解决上述问题 。在实际操作中 , 将所有模板共有的页面布局部分移至基础模板中 , 其他模板则继承自它 。
实例:实现一个简单的导航栏 , 其他模板继承它 。在app/templates目录下创建一个基础模板文件 base.html 。app/templates/base.html:带导航栏的基础模板
<html><head>{% if title %}<title>{{ title }} - Microblog</title>{% else %}<title>Welcome to Microblog</title>{% endif %}</head><body><div>Microblog:<a href="http://www.mnbkw.com/index">Home</a></div><hr>{% block content %}{% endblock %}</body> </html>在上述基础模板中 , 块block 控制语句用于定义派生模板可自行插入的位置 。块block 被赋予唯一的名字 content , 派生模板在提供其内容时可引用这个名称 。
修改index.html这个模板 , 让其继承base.html模板 。app/templates/index.html:从基础模板继承
{% extends "base.html" %} {% block content %}<h1>Hello,{{ user.username }}!</h1>{% for post in posts %}<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>{% endfor %}{% endblock %}base.html基础模板 实现处理常规页面的结构 , 则派生模板index.html简化大部分内容 。extends语句 建立了两个模板之间的继承关系 , 因此 , Jinja2就会知道:当它被要求渲染index.html时 , 需要将其嵌入base.html中 。这俩模板具有匹配的block语句 名称content , 这就是Jinja2如何将两个模板合并为一个模板的方法 。
今后 , 当再需要为应用程序创建其他页面时 , 就可省去编写相同代码的麻烦 , 并让应用程序的所有页面共享相同的外观 , 而只需一个步骤:创建继承自base.html模板的派生模板 。
目前为止 , 项目结构:
microblog/venv/app/templates/base.htmlindex.html__init__.pyroutes.pymicroblog.py