无知的 tonyseek

Yet Another Seeker

对 Python Web 框架 Flask 的一些个人评价

Python 多到数不过来的 Web 框架已经成为了一大风景,而且不同于 PHP Frameworks 集体山寨 Rails 的风格,几乎每个 Python 框架都有自己的特色。我接触过的有 web.py、Django、Bottle、Flask ,其中属 Flask 最为我喜欢。有时候框架会被称为“轮子”,但是可以确定的一点是这四个框架一定不是轮子,我最喜欢的 Flask 有许多非常方便的特性,当然也有我想吐槽的不爽点。于是写一篇博客把吐槽记录下来。

闪亮亮的点

松耦合的组件

这点是 Flask 和 Django 最不同的地方,冲着这点甚至都不应该把 Flask 叫“框架”(Framework),应该叫“库”(Library)。在使用 Flask 的时候,如果控制得合适,可以让 Flask 只和 Web 层面耦合,其他方面不仅可以不受 Flask 影响,还能任意使用 PyPI 上数量庞大的第三方库。

灵活的 Thread Local 模式

很多 Web 框架(比如 web.py、tornado)用类的方式来做 Web 表现层的控制器,请求上下文资源以类成员的形式提供。这样虽然看起来很“面向对象”,却不利于良好地组织代码结构。Web 层除了表现层还会有许多其他自己编写的工具,比如负责用户登录、注销的服务组件,这些组件多多少少都要操纵 Web 上下文。类控制器需要通过传递上下文对象来达到这一目的,这种传递方式会给开发造成不必要的麻烦,到哪里都要给 Context 留个座位。

很庆幸 Flask 没有采取这种方式,而是使用了 ThreadLocal 模式。这有点类似于 PHP 的原理,每个请求线程内部拥有独立的变量空间,用来保存上下文,请求内可以非常安全地操作这些上下文。

from flask import request

@app.route("/")
def home():
    username = request.cookies.get('username')
    return "hello, %s" % username

反向路由

反向路由又是 Flask 的一亮点功能,很多框架(比如 Bottle)虽然提供了类似功能,却设计的比较鸡肋。有了这个功能,基本可以告别硬编码 URL 了。

from flask import url_for, render_template

@app.route("/")
def home():
    login_uri = url_for("login", next=url_for("home"))
    return render_template("home.html", **locals())

蓝图(Blueprint)

Blueprint 类似于 Django 的 App 概念,是 Web 应用中的“子模块”。不同于 Django App 或者 web.py subapplication 的是,Flask 的 Blueprint 同时支持整合静态资源。这一点是通过配合使用 url_for 反向路由做到的。例如应用中有一个 admin Blueprint,其静态资源的 URI 可以通过 url_for("admin.static", "/scripts/admin.js") 获取,这样就不用分离了模块代码却将静态资源放在一起了。

每一个 Blueprint 对象,对于模块内来说,就像是一个独立的 Flask Application,拆卸、复用都非常方便。

想吐槽的不爽点

模板引擎

前文说了 Flask 有着松耦合的组件,但有一个例外,就是模板引擎。我实在理解不了为何 Flask 要把模板绑死在 Jinja 2 上。Flask 文档中的说法是因为使用了 Jinja 2 独有的高级特性,例如过滤器,可问题是这些高级特性确确实实不是 Jinja 2 独有的,Mako 一样有。做一个适配器应该不是难事,可是 Flask 却选择只绑定自家的 Jinja 2。

Jinja 2 的确是很优秀的模板引擎,可是强制使用 Jinja 2 就让开发者做选择的权利被剥夺了。Mako 等模板是另一种风格,支持直接在模板中写 Python 代码,在不少地方更加灵活,完全有理由给开发者选择的机会。支持 Mako 的第三方 Flask 插件也是存在的,可是因为不是直接嵌入 Flask Application,和其他 Flask 插件的兼容很是问题。

Comments