Flask蓝本理解 - 形象生动

作者:杜志鹏

链接:https://www.zhihu.com/question/31748237/answer/55313054

来源:知乎

著作权归作者所有,转载请联系作者获得授权。

分白话版和专业版 :)

白话版

恩,理解「蓝本」对于没有接触其他Web编程实现的Python+Flask新手而言,是一个不好迈过的坎儿。

我得用「比喻」的方式穿插讲述一下「蓝本」,方便你更好理解,问这个问题估计是新手,所以我啰嗦点好了。

首先,提一下概念,「蓝本」的对应官方词汇是「Blueprint」,中译还有「蓝图」这种叫法,是一种东西。

你去餐馆吃饭,点一些菜,你一定吃过「麻辣香锅」这种东西。你可以点一些杂七杂八的菜,选好口味,之后一个碗儿端上来,你吃的很滋味,并且也会觉得这种「一锅端」的方式很好,毕竟不会一道吃完等下一道啊,一起上来热乎乎的多好。并且吃的很少的时候,一点点的菜单独用一个碗儿装也是有点「装逼」的事情。

假如你一个人一己之力要写一个BBS(论坛系统)的话,上面的这个例子,其真实写照是初期功能很弱时,可能页面不多,实现的功能也很少。可能首页就是一个帖子列表,允许用户发布帖子,并且不涉及登录退出、会员等级、权限管理等,你想着,那是后面再说的事情。于是,你将网站主干代码写在一个run.py文件里,让网站成功的跑了起来,你觉得这样实现很方便,快捷。

如果我让你这个时候把run.py理一下,能独立出来的独立成其他的文件,之后在run.py里导入,你要骂我「装逼」了,没事找事。是的,「蓝本」并不适合用来构建想当简单的网站系统。

然而,你胃口变大了,你也有了好朋友,你们要一起去吃饭。于是你遇到一些问题:

你的朋友不喜欢吃某些东西,但你喜欢吃。你朋友喜欢吃的一些东西,你不喜欢吃。

人多了,点的东西多了,你们想如果吃不完可以打包明天吃,但是你们打包还是要挑一些菜打包的,毕竟有些菜隔夜就味道不好,容易坏掉。

你和你的朋友都想先吃一种东西,然后再吃一种东西。比如先吃点荤的,然后吃点素的解解荤。

以上,如果你们还去吃「麻辣香锅」,就都不开心了,你想想看。

类似的,当你的BBS系统越来越复杂,有了登录退出、会员等级、权限管理等等功能后,问题迎面而来:

run.py文件肯定越来越大,代码行数越来越多。你网站所有的路由写在一个文件里,功能也在一个文件里,很容易出现错误,并且难以定位。当然,你如果没实战经验,确实不能切身体会这点。那么我说另一个显而易见的问题:import(导入)和config(配置)一般写在头部,你的新路由按顺序怎么也得写在几百行开外的地方,于是你编程时一下看代码文件最上面,一下拖到底部继续写,这就够「操蛋」的了。

大型网站一定是多人编程,如果此刻安排你做登录退出功能,你的朋友做权限管理功能,那么老板当然不容忍你干完你朋友才开工,一起开干啊!可是此时只有run.py一个文件,没错,你和你的基友可以各自拷贝一份run.py各自开始写,结果你的代码从第100行开始写,你的朋友也从100行开始写,然后你们还各自在头部import了一些新东西。等写好了,怎么办?线上就一个run.py,这下「糟糕」了,苦苦「合并」代码吗?这更「操蛋」了。如果10个人写网站呢?——「蛋蛋艹碎了」

所以,你此刻第一反应当然是「麻辣香锅」吃不成了,这「一碗菜是一碗菜」的方式是最佳的解决方案,爱吃什么吃什么,爱打包哪碗直接装起来,不用拿着筷子挑。

当你利用Python+Flask构建一个网站时,人为的提炼一些代码独立成文件是「不妥」的方式,你需要Flask本身给予一些支持,于是「蓝本」出现了。

「蓝本」允许你将不同路由分开,提供一些「规范(标准)」,并且附带了很多好处。你可以要求商家不同的菜上来装不同的盘子,就像你也可以要求「蓝本」针对不同路由应用不同静态资源,导致不同的URL出现不一样的网站界面(否则前端CSS就混杂了)。

「蓝本」使得你和基友一起工作时不会再有上面的麻烦事,你改你的A文件,他改他的B文件,回头各自提交各自的,多好。

「蓝本」让你做另一个程序时,如果那个程序也有登录退出,理想情况下你甚至可以直接将BBS负责登录退出的部分直接挪过去用,这远比直接从一个run.py里复制出代码现实的多,要知道即使一个登录退出的功能,你在一个run.py里实现,代码会散落在各处的。

「蓝本」还有其他好处,其实本质上来说就是让程序更加松耦合,更加灵活,增加复用性,提高查错效率,降低出错概率。

专业版

正常情况下,入门Flask框架都是从写一个单文件,然后看到页面显示 Hello,World! 开始的。这个单文件一般命名hello.py或run.py。

你会在单文件中写一些路由,比如首页的、列表页的、详情页的,就像我博客这么简单的页面结构,完全可以一个文件搞定。

# -*- coding: utf-8 -*-# run.pyfromflaskimportFlask,render_template,requestfromflask_flatpagesimportFlatPagesFLATPAGES_AUTO_RELOAD=TrueFLATPAGES_ROOT='pages'FLATPAGES_EXTENSION='.md'app=Flask(__name__)app.config.from_object(__name__)flatpages=FlatPages(app)@app.route('/')defindex():returnrender_template('index.html')@app.route('/list//')defpage_list(tag):articles=(pforpinflatpagesiftaginp['tags'])pages=sorted(articles,reverse=True,key=lambdap:p.meta['date'])returnrender_template('list.html',tag=tag,pages=pages)@app.route('/page//')defpage(uri):p=flatpages.get_or_404(uri)returnrender_template('page.html',p=p)if__name__=='__main__':app.run()

上面是一个可以运行的微型博客的全部后端程序,几乎就是你现在正常访问的博客的全部,你看,非常简短!

如果你用过类似WordPress或者新浪博客等博客工具,显然有一个问题:哪儿可以添加新的文章和管理文章分类?的确,这段代码离一个传统的博客程序该有的全貌相差甚远,因为还欠缺一个管理后台。

以WordPress为例子,在一般情况下,如果访问如http://xxx.com,可以看到博客的前台,也就是各种博客文章。而访问如http://xxx.com/wp-admin则可以进入到后台,用来管理文章和发布新文章。在路由的写法上,对于上面这个文件你可能要这样扩充一下功能(为了节省篇幅方便演示,下面代码详细部分省略,以 pass 代替详细部分)。

# -*- coding: utf-8 -*-# run.pyfrom flask import Flask, render_template, requestfrom flask_flatpages import FlatPagesFLATPAGES_AUTO_RELOAD = TrueFLATPAGES_ROOT = 'pages'FLATPAGES_EXTENSION = '.md'app = Flask(__name__)app.config.from_object(__name__)flatpages = FlatPages(app)@app.route('/')def index():pass  # 为了方便演示这里省略详细代码@app.route('/list//')def page_list(tag):pass@app.route('/page//')

def page(uri):

pass

# 新增的后台部分代码

from adminPages import admin

app.register_blueprint(admin, url_prefix='/admin')

if __name__ == '__main__':

app.run()

上述代码新增加了管理后台部分,具体是增加了一个后台登录界面、文章列表以及新增和编辑文章的功能共四个部分。

继续上述思路,可以想到新的几个问题:

1)一般情况下,前台和后台用两套模板。或者通俗的讲,前台费力弄得好看点,后台反正自己用,能用就成,丑点无所谓。那么怎么让前台和后台用两套模板?

2)后台部分逻辑比前台复杂,还需要导入新的包,如果和前台写在一个文件里,后面修改会不会容易出错,例如本来改后台部分结果牵连前台出问题?

3)既然Python力求简洁,那代码能否再简洁些?比如新增的路由参数 /admin 重复写了4遍,能不能对后台定义一个前缀,后台部分的自动加这个/admin ?

4)如果这个博客程序需要多人来维护,多人编辑同一个文件去提交时冲突如何解决?

亦或者你还有其他的一些疑问,如果是关乎怎么把程序做大的同时,还能保证简洁、良好、易于维护的代码组织结构,一个容易想到的方向就是程序的「模块化」。

在你学习Python的过程中,早已见识到了Python模块化,比如Python的包概念,也即你总是能看到的 import …… 引入的那些东西和本身的这个引入方法。那么,对于Flask而言,你也许会举一反三的把博客的后台程序部分抽离出来,单独编写一个文件,然后再 import ……  ,这样不就模块化了?!

但是等等,程序跑不起来了?还有,前面刚提到过的四个问题,似乎这样做之后只是勉强解决了第四个问题,前三个呢?

所以,可想而知,我们 的确需要一个模块化的方法,这个思路的方向本身不错,要点在于我们需要的是一个Flask认可的、兼容的、支持的模块化方法。Flask的作者作为一个老江湖早就考虑到这点,并提供了一个名为Blueprint的方法来让你实现模块化组织程序结构。

Blueprint中文的翻译现在有两种常见的:蓝图以及蓝本。这里我们称蓝图好了。

对于上述程序,我们应用蓝图方法修改之后,大致是下面这样了。

首先,我们新增一个文件adminPages.py,如下:

# -*- coding: utf-8 -*-# adminPages.pyfromflaskimportBlueprintadmin=Blueprint('admin',__name__)@admin.route('/login/')defadmin_login():pass@admin.route('/page/')defadmin_pages():pass@admin.route('/page/new/')defnew_page():pass@admin.route('/page/edit/')defedit_page():pass

同时,改写run.py 如下:

# -*- coding: utf-8 -*-# run.pyfromflaskimportFlask,render_template,requestfromflask_flatpagesimportFlatPagesFLATPAGES_AUTO_RELOAD=TrueFLATPAGES_ROOT='pages'FLATPAGES_EXTENSION='.md'app=Flask(__name__)app.config.from_object(__name__)flatpages=FlatPages(app)@app.route('/')defindex():pass# 为了方便演示这里省略详细代码@app.route('/list//')defpage_list(tag):pass@app.route('/page//')defpage(uri):pass# 新增的后台部分代码fromadminPagesimportadminapp.register_blueprint(admin,url_prefix='/admin')if__name__=='__main__':app.run()

好,这就完成了一个应用蓝图的简单例子。让我们再来回顾上述代码,看看都做了些什么。

首先,我们新增了一个adminPages.py的文件,并在文件中导入了Flask的蓝图方法Blueprint。

紧接着,我们用admin实例化了一个蓝图应用,就是这句:

admin=Blueprint('admin',__name__)

Blueprint要求至少传入两个参数,分别是蓝图的名字和蓝图所在的包或模块。

我们将后台部分独立出来作为蓝图模块,并取名为 admin ,所以我们传入第一个参数'admin'。

紧接着是所在的包或模块,如果你有印象,大概还记得Python有一个特殊属性_ name _ ,当用在一个文件里时,代表的是模块(或包)的名字,所以第二个参数传入 _ name _ 。

因为使用了蓝图,原来的路由 @app.route() 改写成了 @admin.route(),改写的原因你需要看一下官方文档了解更为深入的细节,这里暂且不表。

对于run.py,我们剥离出了 admin 部分的程序代码,但在文件末尾,又导入了蓝图模块,然后按照Flask蓝图的用法去注册了蓝图,就是这两行代码:

fromadminPagesimportadminapp.register_blueprint(admin,url_prefix='/admin')

其中,app.register_blueprint()的第一个参数无需过多解释,第二个参数 url_prefix= 即在定义前缀。还记得前面的四个问题吗?这里针对的就是第三个问题。再回过头去看看,你会发现adminPages.py里,原来路由里的 /admin 都去掉了。

就是这些,我们简单的应用了一把Flask的蓝图功能,一定程度上算是解决了前面四个问题中的三个,还有一个两套模板的问题蓝图也支持,并且十分简单,留给你去看看官方文档了解吧。

提示:template_folder=

以上,我所说的针对「小白」新手,如果你有一定的实践经验,官方的文档写得更准确些:使用蓝图的模块化应用

希望我的回答帮你绕好弯子。: P

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 22年12月更新:个人网站关停,如果仍旧对旧教程有兴趣参考 Github 的markdown内容[https://...
    tangyefei阅读 35,170评论 22 257
  • 解释1: 允许将应用组织为模块,每个模块有自洽的 MVC,开发者做些工作可以使模块间依赖尽可能少,必要时可以按 b...
    大诗兄_zl阅读 2,199评论 1 1
  • flask是python的一个web应用框架,django很多人听过,flask比较少见,连创始人一开始写出来只是...
    思而忧阅读 2,937评论 0 5
  • 第七章 大型程序架构 虽然在一个脚本里完成一个web应用很便利,但是这也意味着它很难扩展。当程序不断增长,越来越复...
    易木成华阅读 913评论 0 1
  • 什么是蓝图? 一个蓝图定义了视图,模板,静态文件以及可以用于应用程序的其它元素的集合。例如,让我们假设下我们有一个...
    邪恶的Sheldon阅读 1,223评论 0 1